Skip to content

Commit 1f5fd30

Browse files
authored
feat(job-run-execution-summary-api): allow unfinished jobs in execution summary (#589)
* add job run summary get state & is finished * allow unfinished job to appear in job run execution summary * readd number of upstreams per level * add truncated & total nodes in response, do not deprecate number of upstream per level * update makefile
1 parent dab7164 commit 1f5fd30

13 files changed

Lines changed: 2540 additions & 1117 deletions

File tree

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ NAME = "github.com/goto/optimus"
55
LAST_COMMIT := $(shell git rev-parse --short HEAD)
66
LAST_TAG := "$(shell git rev-list --tags --max-count=1)"
77
OPMS_VERSION := "$(shell git describe --tags ${LAST_TAG})-next"
8-
PROTON_COMMIT := "bba736bdc93dabd74e2ce2ec155cee99692c0268"
8+
PROTON_COMMIT := "ee54e908094ca84e49c9d5f171ce62f3173281ae"
99

1010

1111
.PHONY: build test test-ci generate-proto unit-test-ci integration-test vet coverage clean install lint

config/config_server.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ type JobExpectatorConfig struct {
199199

200200
type JobExecutionSummaryConfig struct {
201201
MaxLineageDepth int `mapstructure:"max_lineage_depth" default:"25"`
202+
LineageWindowHours int `mapstructure:"lineage_window_hours" default:"24"`
202203
HistoricalDuration HistoricalDurationConfig `mapstructure:"historical_duration"`
203204
}
204205

config/loader_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,8 @@ func (s *ConfigTestSuite) initExpectedServerConfig() {
322322
},
323323
}
324324
s.expectedServerConfig.JobExecutionSummaryConfig = config.JobExecutionSummaryConfig{
325-
MaxLineageDepth: 25,
325+
MaxLineageDepth: 25,
326+
LineageWindowHours: 24,
326327
HistoricalDuration: config.HistoricalDurationConfig{
327328
LastNRuns: 7,
328329
Percentile: 95,

core/scheduler/handler/v1beta1/job_run.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ type JobRunService interface {
6868
}
6969

7070
type JobLineageService interface {
71-
GetJobExecutionSummary(ctx context.Context, jobSchedules []*scheduler.JobSchedule, numberOfUpstreamPerLevel int) ([]*scheduler.JobRunLineage, error)
71+
GetJobExecutionSummary(ctx context.Context, jobSchedules []*scheduler.JobSchedule, opts scheduler.LineageSummaryOptions) ([]*scheduler.JobRunLineage, error)
7272
}
7373

7474
type ThirdPartySensorService interface {
@@ -590,7 +590,11 @@ func (h JobRunHandler) GetJobRunLineageSummary(ctx context.Context, req *pb.GetJ
590590
h.l.Error("error parsing job schedules from request: %s", err)
591591
return nil, errors.GRPCErr(err, "unable to parse job schedules from request")
592592
}
593-
jobRunLineages, err := h.jobLineageService.GetJobExecutionSummary(ctx, targetJobSchedules, int(req.GetNumberOfUpstreamPerLevel()))
593+
jobRunLineages, err := h.jobLineageService.GetJobExecutionSummary(ctx, targetJobSchedules, scheduler.LineageSummaryOptions{
594+
MaxNodes: int(req.GetMaxNodes()),
595+
TopUpstreamsPerJob: int(req.GetNumberOfUpstreamPerLevel()),
596+
WindowHours: int(req.GetLineageWindowHours()),
597+
})
594598
if err != nil {
595599
h.l.Error("error getting job run lineage summary: %s", err)
596600
return nil, errors.GRPCErr(err, "unable to get job run lineage summary")

core/scheduler/handler/v1beta1/job_run_adapter.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@ import (
1414
pb "github.com/goto/optimus/protos/gotocompany/optimus/core/v1beta1"
1515
)
1616

17+
var jobRunStateToProto = map[scheduler.State]pb.JobRunState{
18+
scheduler.StateNotScheduled: pb.JobRunState_JOB_RUN_STATE_NOT_SCHEDULED,
19+
scheduler.StateWaitUpstream: pb.JobRunState_JOB_RUN_STATE_WAIT_UPSTREAM,
20+
scheduler.StateRunning: pb.JobRunState_JOB_RUN_STATE_RUNNING,
21+
scheduler.StateSuccess: pb.JobRunState_JOB_RUN_STATE_SUCCESS,
22+
scheduler.StateFailed: pb.JobRunState_JOB_RUN_STATE_FAILED,
23+
}
24+
1725
// buildIdentifySLABreachInputs resolves an IdentifyPotentialSLABreachRequest into
1826
// the combos (cross product of projects x label groups) to evaluate and the
1927
// shared predictor config. job_names only applies in legacy single-project mode,
@@ -137,6 +145,30 @@ func fromJobRunLineageSummaryRequest(req *pb.GetJobRunLineageSummaryRequest) ([]
137145
return targetJobSchedules, nil
138146
}
139147

148+
func toJobRunStateProto(state scheduler.State) pb.JobRunState {
149+
if pbState, ok := jobRunStateToProto[state]; ok {
150+
return pbState
151+
}
152+
153+
return pb.JobRunState_JOB_RUN_STATE_UNSPECIFIED
154+
}
155+
156+
func toJobRunRefsProto(refs []scheduler.JobRunKey) []*pb.JobRunRef {
157+
if len(refs) == 0 {
158+
return nil
159+
}
160+
161+
pbRefs := make([]*pb.JobRunRef, 0, len(refs))
162+
for _, ref := range refs {
163+
pbRefs = append(pbRefs, &pb.JobRunRef{
164+
JobName: ref.JobName.String(),
165+
ScheduledAt: timestamppb.New(ref.ScheduledAt),
166+
})
167+
}
168+
169+
return pbRefs
170+
}
171+
140172
func toJobRunLineageSummaryResponse(jobRunLineages []*scheduler.JobRunLineage) *pb.GetJobRunLineageSummaryResponse {
141173
var pbJobRunLineages []*pb.JobRunLineageSummary
142174
for _, lineage := range jobRunLineages {
@@ -224,6 +256,9 @@ func toJobRunLineageSummaryResponse(jobRunLineages []*scheduler.JobRunLineage) *
224256
},
225257
Level: int32(run.Level),
226258
DownstreamPathName: run.DownstreamPathName,
259+
RunState: toJobRunStateProto(run.State),
260+
IsBlocking: run.IsBlocking,
261+
DownstreamRefs: toJobRunRefsProto(run.DownstreamRefs),
227262
}
228263
if run.DelaySummary != nil {
229264
pbJobRun.DelaySummary = &pb.JobRunDelaySummary{
@@ -261,6 +296,8 @@ func toJobRunLineageSummaryResponse(jobRunLineages []*scheduler.JobRunLineage) *
261296
JobName: lineage.JobName.String(),
262297
ScheduledAt: timestamppb.New(lineage.JobRuns[0].JobRunSummary.ScheduledAt),
263298
JobRuns: pbJobRuns,
299+
TotalNodes: int32(lineage.TotalNodes),
300+
Truncated: lineage.Truncated,
264301
ExecutionSummary: &pb.LineageExecutionSummary{
265302
TotalScheduledWayTooLateSeconds: int32(lineage.ExecutionSummary.TotalScheduledWayTooLateSeconds),
266303
TotalSystemSchedulingDelaySeconds: int32(lineage.ExecutionSummary.TotalSystemSchedulingDelaySeconds),

core/scheduler/handler/v1beta1/job_run_test.go

Lines changed: 73 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -991,7 +991,7 @@ func TestJobRunHandler(t *testing.T) {
991991
NumberOfUpstreamPerLevel: 5,
992992
}
993993

994-
jobLineageService.On("GetJobExecutionSummary", ctx, mock.Anything, 5).
994+
jobLineageService.On("GetJobExecutionSummary", ctx, mock.Anything, scheduler.LineageSummaryOptions{TopUpstreamsPerJob: 5}).
995995
Return(nil, errors.New("service error"))
996996

997997
resp, err := handler.GetJobRunLineageSummary(ctx, req)
@@ -1036,14 +1036,83 @@ func TestJobRunHandler(t *testing.T) {
10361036
},
10371037
}
10381038

1039-
jobLineageService.On("GetJobExecutionSummary", ctx, mock.Anything, 5).
1039+
jobLineageService.On("GetJobExecutionSummary", ctx, mock.Anything, scheduler.LineageSummaryOptions{TopUpstreamsPerJob: 5}).
10401040
Return(mockLineages, nil)
10411041

10421042
resp, err := handler.GetJobRunLineageSummary(ctx, req)
10431043
assert.Nil(t, err)
10441044
assert.NotNil(t, resp)
10451045
assert.Equal(t, len(mockLineages), len(resp.Jobs))
10461046
})
1047+
1048+
t.Run("should prefer max_nodes over the deprecated per-level field and pass the window", func(t *testing.T) {
1049+
jobRunService := new(mockJobRunService)
1050+
jobLineageService := new(mockJobLineageService)
1051+
defer jobLineageService.AssertExpectations(t)
1052+
1053+
handler := v1beta1.NewJobRunHandler(logger, jobRunService, nil, nil, jobLineageService, nil, nil, nil)
1054+
1055+
req := &pb.GetJobRunLineageSummaryRequest{
1056+
TargetJobs: []*pb.TargetJobRunIdentifier{{JobName: "test-job", ScheduledAt: timestamppb.Now()}},
1057+
NumberOfUpstreamPerLevel: 5,
1058+
MaxNodes: 40,
1059+
LineageWindowHours: 10,
1060+
}
1061+
1062+
jobLineageService.On("GetJobExecutionSummary", ctx, mock.Anything, scheduler.LineageSummaryOptions{MaxNodes: 40, TopUpstreamsPerJob: 5, WindowHours: 10}).
1063+
Return([]*scheduler.JobRunLineage{}, nil)
1064+
1065+
_, err := handler.GetJobRunLineageSummary(ctx, req)
1066+
assert.Nil(t, err)
1067+
})
1068+
1069+
t.Run("should map run state, blocking flag and downstream refs onto the response", func(t *testing.T) {
1070+
jobRunService := new(mockJobRunService)
1071+
jobLineageService := new(mockJobLineageService)
1072+
defer jobLineageService.AssertExpectations(t)
1073+
1074+
handler := v1beta1.NewJobRunHandler(logger, jobRunService, nil, nil, jobLineageService, nil, nil, nil)
1075+
1076+
scheduledAt := timestamppb.Now()
1077+
upstreamScheduledAt := scheduledAt.AsTime().Add(-1 * time.Hour)
1078+
req := &pb.GetJobRunLineageSummaryRequest{
1079+
TargetJobs: []*pb.TargetJobRunIdentifier{{JobName: "test-job", ScheduledAt: scheduledAt}},
1080+
}
1081+
1082+
mockLineages := []*scheduler.JobRunLineage{
1083+
{
1084+
JobName: "test-job",
1085+
ScheduledAt: scheduledAt.AsTime(),
1086+
JobRuns: []*scheduler.JobExecutionSummary{
1087+
{
1088+
JobName: "upstream-job",
1089+
State: scheduler.StateRunning,
1090+
IsBlocking: true,
1091+
DownstreamRefs: []scheduler.JobRunKey{
1092+
{JobName: "test-job", ScheduledAt: upstreamScheduledAt},
1093+
},
1094+
JobRunSummary: &scheduler.JobRunSummary{
1095+
JobName: "upstream-job",
1096+
ScheduledAt: upstreamScheduledAt,
1097+
},
1098+
},
1099+
},
1100+
ExecutionSummary: &scheduler.LineageExecutionSummary{},
1101+
},
1102+
}
1103+
1104+
jobLineageService.On("GetJobExecutionSummary", ctx, mock.Anything, scheduler.LineageSummaryOptions{}).Return(mockLineages, nil)
1105+
1106+
resp, err := handler.GetJobRunLineageSummary(ctx, req)
1107+
assert.Nil(t, err)
1108+
1109+
pbRun := resp.Jobs[0].JobRuns[0]
1110+
assert.Equal(t, pb.JobRunState_JOB_RUN_STATE_RUNNING, pbRun.RunState)
1111+
assert.True(t, pbRun.IsBlocking)
1112+
assert.Len(t, pbRun.DownstreamRefs, 1)
1113+
assert.Equal(t, "test-job", pbRun.DownstreamRefs[0].JobName)
1114+
assert.Equal(t, upstreamScheduledAt.UTC(), pbRun.DownstreamRefs[0].ScheduledAt.AsTime().UTC())
1115+
})
10471116
})
10481117

10491118
t.Run("GenerateExpectedFinishTime", func(t *testing.T) {
@@ -1135,8 +1204,8 @@ type mockJobLineageService struct {
11351204
mock.Mock
11361205
}
11371206

1138-
func (m *mockJobLineageService) GetJobExecutionSummary(ctx context.Context, jobSchedules []*scheduler.JobSchedule, numberOfUpstreamPerLevel int) ([]*scheduler.JobRunLineage, error) {
1139-
args := m.Called(ctx, jobSchedules, numberOfUpstreamPerLevel)
1207+
func (m *mockJobLineageService) GetJobExecutionSummary(ctx context.Context, jobSchedules []*scheduler.JobSchedule, opts scheduler.LineageSummaryOptions) ([]*scheduler.JobRunLineage, error) {
1208+
args := m.Called(ctx, jobSchedules, opts)
11401209
if args.Get(0) == nil {
11411210
return nil, args.Error(1)
11421211
}

0 commit comments

Comments
 (0)