Skip to content

Commit f200174

Browse files
committed
Show inconsistent scheduler state errors in dagit
Summary: Show consistenty issues with cron tab and schedule state in the UI Test Plan: manual Reviewers: alangenfeld Reviewed By: alangenfeld Differential Revision: https://dagster.phacility.com/D3043
1 parent 8305eba commit f200174

16 files changed

Lines changed: 122 additions & 32 deletions

File tree

CHANGES.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,16 @@
88
the previous pipeline failed or not.
99
- Added a search filter for the root Assets page
1010
- Adds tooltip explanations for disabled run actions
11+
- The last output of the cron job command created by the scheduler is now stored in a file. A new `dagster schedule logs {schedule_name}` command will show the log file for a given schedule. This helps uncover errors like missing environment variables and import errors.
12+
- The dagit schedule page will now show inconsistency errors between schedule state and the cron tab that were previously only displayed by the `dagster schedule debug` command. As before, these errors can be resolve using `dagster schedule up`
1113

1214
**Bugfix**
1315

1416
- Fixes an issue with config schema validation on Arrays
1517
- Fixes an issue with initializing K8sRunLauncher when configured via `dagster.yaml`
1618
- Fixes a race condition in Airflow injection logic that happens when multiple Operators try to
1719
create PipelineRun entries simultaneously.
20+
- Fixed an issue with schedules that had invalid config not logging the appropriate error.
1821

1922
## 0.7.13
2023

js_modules/dagit/src/schedules/ScheduleRoot.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -137,9 +137,12 @@ const RenderEventSpecificData: React.FunctionComponent<{
137137
minimal={true}
138138
href={`/runs/${data.run?.pipeline.name}/${data.run?.runId}`}
139139
>
140-
<Tag fill={true} minimal={true} intent={Intent.SUCCESS}>
141-
Run {data.run?.runId}
142-
</Tag>
140+
<div style={{ display: "flex" }}>
141+
{data.run?.status && <RunStatus status={data.run?.status} />}
142+
<Tag fill={true} minimal={true} style={{ marginLeft: 10 }}>
143+
Run {data.run?.runId}
144+
</Tag>
145+
</div>
143146
</AnchorButton>
144147
);
145148
}
@@ -152,7 +155,7 @@ const TickTag: React.FunctionComponent<{ status: ScheduleTickStatus }> = ({
152155
case ScheduleTickStatus.STARTED:
153156
return (
154157
<Tag minimal={true} intent={Intent.PRIMARY}>
155-
Success
158+
Started
156159
</Tag>
157160
);
158161
case ScheduleTickStatus.SUCCESS:
@@ -339,6 +342,7 @@ export const SCHEDULE_ROOT_QUERY = gql`
339342
pipeline {
340343
name
341344
}
345+
status
342346
runId
343347
}
344348
}

js_modules/dagit/src/schedules/ScheduleRow.tsx

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ import {
1313
Popover,
1414
Tooltip,
1515
Tag,
16-
Intent
16+
Intent,
17+
PopoverInteractionKind,
18+
Position
1719
} from "@blueprintjs/core";
1820
import { HighlightedCodeBlock } from "../HighlightedCodeBlock";
1921
import { RowColumn, RowContainer } from "../ListComponents";
@@ -51,12 +53,60 @@ const getNaturalLanguageCronString = (cronSchedule: string) => {
5153
}
5254
};
5355

56+
const errorDisplay = (status: ScheduleStatus, runningJobCount: number) => {
57+
if (status === ScheduleStatus.STOPPED && runningJobCount == 0) {
58+
return null;
59+
} else if (status === ScheduleStatus.RUNNING && runningJobCount == 1) {
60+
return null;
61+
}
62+
63+
const errors = [];
64+
if (status === ScheduleStatus.RUNNING && runningJobCount === 0) {
65+
errors.push(
66+
"Schedule is set to be running, but the scheduler is not running the schedule"
67+
);
68+
} else if (status === ScheduleStatus.STOPPED && runningJobCount > 0) {
69+
errors.push(
70+
"Schedule is set to be stopped, but the scheduler is still running the schedule"
71+
);
72+
}
73+
74+
if (runningJobCount > 0) {
75+
errors.push("Duplicate cron job for schedule found.");
76+
}
77+
78+
return (
79+
<Popover
80+
interactionKind={PopoverInteractionKind.CLICK}
81+
popoverClassName="bp3-popover-content-sizing"
82+
position={Position.RIGHT}
83+
fill={true}
84+
>
85+
<Tag fill={true} interactive={true} intent={Intent.DANGER}>
86+
Error
87+
</Tag>
88+
<div>
89+
<h3>There are errors with this schedule.</h3>
90+
91+
<p>To resolve, run `dagster schedule up`.</p>
92+
<p>Errors:</p>
93+
<ul>
94+
{errors.map((error, index) => (
95+
<li key={index}>{error}</li>
96+
))}
97+
</ul>
98+
</div>
99+
</Popover>
100+
);
101+
};
102+
54103
export const ScheduleRow: React.FunctionComponent<{
55104
schedule: ScheduleFragment;
56105
}> = ({ schedule }) => {
57106
const {
58107
status,
59108
scheduleDefinition,
109+
runningJobCount,
60110
logsPath,
61111
stats,
62112
ticks,
@@ -143,6 +193,8 @@ export const ScheduleRow: React.FunctionComponent<{
143193
}
144194
}}
145195
/>
196+
197+
{errorDisplay(status, runningJobCount)}
146198
</RowColumn>
147199
<RowColumn style={{ flex: 1.4 }}>{displayName}</RowColumn>
148200
<RowColumn>
@@ -316,6 +368,7 @@ export const ScheduleRow: React.FunctionComponent<{
316368
export const ScheduleRowFragment = gql`
317369
fragment ScheduleFragment on RunningSchedule {
318370
__typename
371+
runningJobCount
319372
scheduleDefinition {
320373
name
321374
cronSchedule
@@ -375,6 +428,7 @@ const START_SCHEDULE_MUTATION = gql`
375428
... on RunningScheduleResult {
376429
schedule {
377430
__typename
431+
runningJobCount
378432
scheduleDefinition {
379433
__typename
380434
name
@@ -397,6 +451,7 @@ const STOP_SCHEDULE_MUTATION = gql`
397451
... on RunningScheduleResult {
398452
schedule {
399453
__typename
454+
runningJobCount
400455
scheduleDefinition {
401456
__typename
402457
name

js_modules/dagit/src/schedules/types/ScheduleFragment.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ export interface ScheduleFragment_stats {
4747

4848
export interface ScheduleFragment {
4949
__typename: "RunningSchedule";
50+
runningJobCount: number;
5051
scheduleDefinition: ScheduleFragment_scheduleDefinition;
5152
logsPath: string;
5253
ticks: ScheduleFragment_ticks[];

js_modules/dagit/src/schedules/types/ScheduleRootQuery.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ export interface ScheduleRootQuery_scheduleOrError_RunningSchedule_ticksList_tic
5959
export interface ScheduleRootQuery_scheduleOrError_RunningSchedule_ticksList_tickSpecificData_ScheduleTickSuccessData_run {
6060
__typename: "PipelineRun";
6161
pipeline: ScheduleRootQuery_scheduleOrError_RunningSchedule_ticksList_tickSpecificData_ScheduleTickSuccessData_run_pipeline;
62+
status: PipelineRunStatus;
6263
runId: string;
6364
}
6465

@@ -111,6 +112,7 @@ export interface ScheduleRootQuery_scheduleOrError_RunningSchedule_attemptList {
111112

112113
export interface ScheduleRootQuery_scheduleOrError_RunningSchedule {
113114
__typename: "RunningSchedule";
115+
runningJobCount: number;
114116
scheduleDefinition: ScheduleRootQuery_scheduleOrError_RunningSchedule_scheduleDefinition;
115117
logsPath: string;
116118
ticks: ScheduleRootQuery_scheduleOrError_RunningSchedule_ticks[];

js_modules/dagit/src/schedules/types/SchedulesRootQuery.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ export interface SchedulesRootQuery_scheduler_Scheduler_runningSchedules_stats {
5252

5353
export interface SchedulesRootQuery_scheduler_Scheduler_runningSchedules {
5454
__typename: "RunningSchedule";
55+
runningJobCount: number;
5556
scheduleDefinition: SchedulesRootQuery_scheduler_Scheduler_runningSchedules_scheduleDefinition;
5657
logsPath: string;
5758
ticks: SchedulesRootQuery_scheduler_Scheduler_runningSchedules_ticks[];

js_modules/dagit/src/schedules/types/StartSchedule.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export interface StartSchedule_startSchedule_RunningScheduleResult_schedule_sche
1616

1717
export interface StartSchedule_startSchedule_RunningScheduleResult_schedule {
1818
__typename: "RunningSchedule";
19+
runningJobCount: number;
1920
scheduleDefinition: StartSchedule_startSchedule_RunningScheduleResult_schedule_scheduleDefinition;
2021
status: ScheduleStatus;
2122
}

js_modules/dagit/src/schedules/types/StopSchedule.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export interface StopSchedule_stopRunningSchedule_RunningScheduleResult_schedule
1616

1717
export interface StopSchedule_stopRunningSchedule_RunningScheduleResult_schedule {
1818
__typename: "RunningSchedule";
19+
runningJobCount: number;
1920
scheduleDefinition: StopSchedule_stopRunningSchedule_RunningScheduleResult_schedule_scheduleDefinition;
2021
status: ScheduleStatus;
2122
}

js_modules/dagit/src/schema.graphql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1003,6 +1003,7 @@ type RunningSchedule {
10031003
attempts(limit: Int): [ScheduleAttempt!]!
10041004
attemptsCount: Int!
10051005
logsPath: String!
1006+
runningJobCount: Int!
10061007
}
10071008

10081009
type RunningScheduleResult {

python_modules/dagster-graphql/dagster_graphql/schema/schedules.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ class Meta(object):
208208
attempts = dauphin.Field(dauphin.non_null_list('ScheduleAttempt'), limit=dauphin.Int())
209209
attempts_count = dauphin.NonNull(dauphin.Int)
210210
logs_path = dauphin.NonNull(dauphin.String)
211+
running_job_count = dauphin.NonNull(dauphin.Int)
211212

212213
def __init__(self, graphene_info, schedule):
213214
self._schedule = check.inst_param(schedule, 'schedule', Schedule)
@@ -222,6 +223,13 @@ def __init__(self, graphene_info, schedule):
222223
repository_path=schedule.repository_path,
223224
)
224225

226+
def resolve_running_job_count(self, graphene_info):
227+
repository = graphene_info.context.get_repository()
228+
running_job_count = graphene_info.context.instance.running_job_count(
229+
repository.name, self._schedule.name
230+
)
231+
return running_job_count
232+
225233
# TODO: Delete in 0.8.0 release
226234
# https://github.com/dagster-io/dagster/issues/228
227235
def resolve_attempts(self, graphene_info, **kwargs):

0 commit comments

Comments
 (0)