Fix pipeline timestamps (#730)

* only calculate time on running builds

* Add updated timestamp into database and use it in frontend

* add more trace logging

* refactor (move grpc unrelated func into related package)

* fix xorm schema

* add todo
This commit is contained in:
6543 2022-01-31 15:38:39 +01:00 committed by GitHub
parent 6af94d79e3
commit 2a5159f7fe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 58 additions and 39 deletions

View file

@ -321,6 +321,12 @@ func (s *RPC) Done(c context.Context, id string, state rpc.State) error {
return err
}
log.Trace().
Str("repo_id", fmt.Sprint(repo.ID)).
Str("build_id", fmt.Sprint(build.ID)).
Str("proc_id", id).
Msgf("gRPC Done with state: %#v", state)
if proc, err = shared.UpdateProcStatusToDone(s.store, *proc, state); err != nil {
log.Error().Msgf("error: done: cannot update proc_id %d state: %s", proc.ID, err)
}
@ -341,8 +347,8 @@ func (s *RPC) Done(c context.Context, id string, state rpc.State) error {
}
s.completeChildrenIfParentCompleted(procs, proc)
if !isThereRunningStage(procs) {
if build, err = shared.UpdateStatusToDone(s.store, *build, buildStatus(procs), proc.Stopped); err != nil {
if !model.IsThereRunningStage(procs) {
if build, err = shared.UpdateStatusToDone(s.store, *build, model.BuildStatus(procs), proc.Stopped); err != nil {
log.Error().Err(err).Msgf("error: done: cannot update build_id %d final state", build.ID)
}
}
@ -361,23 +367,13 @@ func (s *RPC) Done(c context.Context, id string, state rpc.State) error {
s.buildCount.WithLabelValues(repo.FullName, build.Branch, string(build.Status), "total").Inc()
s.buildTime.WithLabelValues(repo.FullName, build.Branch, string(build.Status), "total").Set(float64(build.Finished - build.Started))
}
if isMultiPipeline(procs) {
if model.IsMultiPipeline(procs) {
s.buildTime.WithLabelValues(repo.FullName, build.Branch, string(proc.State), proc.Name).Set(float64(proc.Stopped - proc.Started))
}
return nil
}
func isMultiPipeline(procs []*model.Proc) bool {
countPPIDZero := 0
for _, proc := range procs {
if proc.PPID == 0 {
countPPIDZero++
}
}
return countPPIDZero > 1
}
// Log implements the rpc.Log function
func (s *RPC) Log(c context.Context, id string, line *rpc.Line) error {
entry := new(logging.Entry)
@ -398,31 +394,6 @@ func (s *RPC) completeChildrenIfParentCompleted(procs []*model.Proc, completedPr
}
}
func isThereRunningStage(procs []*model.Proc) bool {
for _, p := range procs {
if p.PPID == 0 {
if p.Running() {
return true
}
}
}
return false
}
func buildStatus(procs []*model.Proc) model.StatusValue {
status := model.StatusSuccess
for _, p := range procs {
if p.PPID == 0 {
if p.Failing() {
status = p.State
}
}
}
return status
}
func (s *RPC) updateRemoteStatus(ctx context.Context, repo *model.Repo, build *model.Build, proc *model.Proc) {
user, err := s.store.GetUser(repo.UserID)
if err != nil {

View file

@ -28,6 +28,7 @@ type Build struct {
Error string `json:"error" xorm:"build_error"`
Enqueued int64 `json:"enqueued_at" xorm:"build_enqueued"`
Created int64 `json:"created_at" xorm:"build_created"`
Updated int64 `json:"updated_at" xorm:"updated NOT NULL DEFAULT 0 'updated'"`
Started int64 `json:"started_at" xorm:"build_started"`
Finished int64 `json:"finished_at" xorm:"build_finished"`
Deploy string `json:"deploy_to" xorm:"build_deploy"`

View file

@ -68,6 +68,20 @@ func (p *Proc) IsParent() bool {
return p.PPID == 0
}
// IsMultiPipeline checks if proc list contain more than one parent proc
func IsMultiPipeline(procs []*Proc) bool {
c := 0
for _, proc := range procs {
if proc.IsParent() {
c++
}
if c > 1 {
return true
}
}
return false
}
// Tree creates a process tree from a flat process list.
func Tree(procs []*Proc) ([]*Proc, error) {
var nodes []*Proc
@ -93,6 +107,32 @@ func Tree(procs []*Proc) ([]*Proc, error) {
return nodes, nil
}
// BuildStatus determine build status based on corresponding proc list
func BuildStatus(procs []*Proc) StatusValue {
status := StatusSuccess
for _, p := range procs {
if p.IsParent() && p.Failing() {
status = p.State
}
}
return status
}
// IsThereRunningStage determine if it contains procs running or pending to run
// TODO: return false based on depends_on (https://github.com/woodpecker-ci/woodpecker/pull/730#discussion_r795681697)
func IsThereRunningStage(procs []*Proc) bool {
for _, p := range procs {
if p.IsParent() {
if p.Running() {
return true
}
}
}
return false
}
func findNode(nodes []*Proc, pid int) (*Proc, error) {
for _, node := range nodes {
if node.PID == pid {

View file

@ -40,13 +40,17 @@ export default (build: Ref<Build | undefined>) => {
}
const start = build.value.started_at || 0;
const end = build.value.finished_at || 0;
const end = build.value.finished_at || build.value.updated_at || 0;
if (start === 0) {
return 0;
}
if (end === 0) {
// only calculate time on running builds
if (build.value.status !== 'running') {
return 0;
}
return Date.now() - start * 1000;
}

View file

@ -18,6 +18,9 @@ export type Build = {
// When the build request was received.
created_at: number;
// When the build was updated last time in database.
updated_at: number;
// When the build was enqueued.
enqueued_at: number;