Skip to content

Cancel the Dataflow job when a user kills the deferred task - #69586

Open
steveahnahn wants to merge 2 commits into
apache:mainfrom
steveahnahn:dataflow-cancel-job-on-user-kill-deferred
Open

Cancel the Dataflow job when a user kills the deferred task#69586
steveahnahn wants to merge 2 commits into
apache:mainfrom
steveahnahn:dataflow-cancel-job-on-user-kill-deferred

Conversation

@steveahnahn

@steveahnahnsteveahnahn commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Problem

  • Killing a deferred Dataflow task (mark failed, clear, mark success) leaves the Dataflow job running and billing in GCP.
  • Neither TemplateJobStartTrigger (templated/flex jobs) nor DataflowStartYamlJobTrigger (YAML jobs) has on_kill; the BigQuery and Dataproc triggers in this provider already cancel in the same situation.
  • The YAML operator's own on_kill even documents the gap: it "will not be called if a task instance is killed in a deferred state."
  • A streaming job runs until someone notices it in the Cloud console.

Change

  • Add on_kill to both TemplateJobStartTrigger and DataflowStartYamlJobTrigger: checks cancel_on_kill, job_id, project_id, then cancels via the sync DataflowHook. Identical cancel path in both.
  • drain_pipeline is honored, matching the non deferred kill path.
  • Hook is constructed and invoked inside a worker thread (sync_to_async): DataflowHook resolves its connection eagerly at construction, which must not run in the triggerer event loop. Construction inside the try means a connection error is logged, not raised.
  • Fires only on user kill, never on triggerer restart, redistribution, or timeout.

End to end verification

  1. Deferrable operator launched the job; task deferred, job JOB_STATE_RUNNING.
deferred task, job running
  1. Marked the task failed from the UI.
marking the deferred task failed
  1. Triggerer log, no exception escaping on_kill:
Trigger cancelled by user action, invoking on_kill
Stopping Dataflow job. Project ID: <redacted>, Location: us-central1, Job ID: 2026-07-08_21_43_34-418737444659630262, drain: False
  1. Job went JOB_STATE_RUNNING -> JOB_STATE_CANCELLING -> JOB_STATE_CANCELLED (gcloud dataflow jobs list). With cancel_on_kill=False the job keeps running.

The YAML trigger uses the identical DataflowHook.cancel_job path proven above, so the e2e run covers its cancel mechanism; it is additionally unit tested.

Tests

  • on_kill tests for both triggers: cancel, drain_pipeline honored, no ops (cancel_on_kill=False, missing job_id/project_id), hook errors swallowed, hook construction errors swallowed; plus operator threading of drain_pipeline into the trigger.
  • All new assertions fail without the change; 95 trigger tests pass with it; provider mypy clean.

Was generative AI tooling used to co-author this PR?
  • Yes, Claude Code (Opus 4.8)

Generated-by: Claude Code (Opus 4.8) following the guidelines

@boring-cyborgboring-cyborgBot added area:providers provider:google Google (including GCP) related issues labels Jul 8, 2026
@steveahnahn
steveahnahnforce-pushed the dataflow-cancel-job-on-user-kill-deferred branch 2 times, most recently from cadba9a to f4df591CompareJuly 8, 2026 06:46
@steveahnahn
steveahnahn marked this pull request as ready for review July 8, 2026 21:02
@steveahnahn
steveahnahn requested a review from shahar1 as a code ownerJuly 8, 2026 21:02
@steveahnahn
steveahnahnforce-pushed the dataflow-cancel-job-on-user-kill-deferred branch from f4df591 to 6e7fa45CompareJuly 10, 2026 23:05
@potiukpotiuk added the ready for maintainer review Set after triaging when all criteria pass. label Jul 11, 2026

@aaron-y-chenaaron-y-chen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, thanks for the PR, very impressive :)

Comment threadproviders/google/src/airflow/providers/google/cloud/operators/dataflow.py Outdated
@steveahnahn
steveahnahnforce-pushed the dataflow-cancel-job-on-user-kill-deferred branch from 6e7fa45 to 54df2f5CompareJuly 16, 2026 02:04
Killing a deferred Dataflow task (mark-failed, clear or mark-succeeded)
leaves the Dataflow job running: the trigger has no on_kill, so the
user-kill cancellation the sibling BigQuery and Dataproc triggers
already perform never happens, and a streaming pipeline keeps running
and billing until someone notices in the Cloud console. Before the
Airflow 3.3 trigger cancellation redesign the legacy path cancelled the
job from inside the trigger, so this is also a behavior regression for
deferrable users.
Implement on_kill on TemplateJobStartTrigger following the merged
BigQuery/Dataproc/EMR pattern, honoring the operator's drain_pipeline
setting so streaming jobs are drained rather than cancelled when
configured, exactly as the non-deferred kill path does. The hook is
built and invoked inside a worker thread because the synchronous
DataflowHook resolves its connection during construction, which must
not run in the triggerer's event loop.
@steveahnahn
steveahnahnforce-pushed the dataflow-cancel-job-on-user-kill-deferred branch from 54df2f5 to bd3f1bbCompareJuly 16, 2026 02:05
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:providersprovider:googleGoogle (including GCP) related issuesready for maintainer reviewSet after triaging when all criteria pass.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@steveahnahn@aaron-y-chen@potiuk
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Cancel the Dataflow job when a user kills the deferred task by steveahnahn · Pull Request #69586 · apache/airflow · GitHub
Skip to content

Cancel the Dataflow job when a user kills the deferred task - #69586

Open
steveahnahn wants to merge 2 commits into
apache:mainfrom
steveahnahn:dataflow-cancel-job-on-user-kill-deferred
Open

Cancel the Dataflow job when a user kills the deferred task#69586
steveahnahn wants to merge 2 commits into
apache:mainfrom
steveahnahn:dataflow-cancel-job-on-user-kill-deferred

Conversation

@steveahnahn

@steveahnahnsteveahnahn commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Problem

  • Killing a deferred Dataflow task (mark failed, clear, mark success) leaves the Dataflow job running and billing in GCP.
  • Neither TemplateJobStartTrigger (templated/flex jobs) nor DataflowStartYamlJobTrigger (YAML jobs) has on_kill; the BigQuery and Dataproc triggers in this provider already cancel in the same situation.
  • The YAML operator's own on_kill even documents the gap: it "will not be called if a task instance is killed in a deferred state."
  • A streaming job runs until someone notices it in the Cloud console.

Change

  • Add on_kill to both TemplateJobStartTrigger and DataflowStartYamlJobTrigger: checks cancel_on_kill, job_id, project_id, then cancels via the sync DataflowHook. Identical cancel path in both.
  • drain_pipeline is honored, matching the non deferred kill path.
  • Hook is constructed and invoked inside a worker thread (sync_to_async): DataflowHook resolves its connection eagerly at construction, which must not run in the triggerer event loop. Construction inside the try means a connection error is logged, not raised.
  • Fires only on user kill, never on triggerer restart, redistribution, or timeout.

End to end verification

  1. Deferrable operator launched the job; task deferred, job JOB_STATE_RUNNING.
deferred task, job running
  1. Marked the task failed from the UI.
marking the deferred task failed
  1. Triggerer log, no exception escaping on_kill:
Trigger cancelled by user action, invoking on_kill
Stopping Dataflow job. Project ID: <redacted>, Location: us-central1, Job ID: 2026-07-08_21_43_34-418737444659630262, drain: False
  1. Job went JOB_STATE_RUNNING -> JOB_STATE_CANCELLING -> JOB_STATE_CANCELLED (gcloud dataflow jobs list). With cancel_on_kill=False the job keeps running.

The YAML trigger uses the identical DataflowHook.cancel_job path proven above, so the e2e run covers its cancel mechanism; it is additionally unit tested.

Tests

  • on_kill tests for both triggers: cancel, drain_pipeline honored, no ops (cancel_on_kill=False, missing job_id/project_id), hook errors swallowed, hook construction errors swallowed; plus operator threading of drain_pipeline into the trigger.
  • All new assertions fail without the change; 95 trigger tests pass with it; provider mypy clean.

Was generative AI tooling used to co-author this PR?
  • Yes, Claude Code (Opus 4.8)

Generated-by: Claude Code (Opus 4.8) following the guidelines

@boring-cyborgboring-cyborgBot added area:providers provider:google Google (including GCP) related issues labels Jul 8, 2026
@steveahnahn
steveahnahnforce-pushed the dataflow-cancel-job-on-user-kill-deferred branch 2 times, most recently from cadba9a to f4df591CompareJuly 8, 2026 06:46
@steveahnahn
steveahnahn marked this pull request as ready for review July 8, 2026 21:02
@steveahnahn
steveahnahn requested a review from shahar1 as a code ownerJuly 8, 2026 21:02
@steveahnahn
steveahnahnforce-pushed the dataflow-cancel-job-on-user-kill-deferred branch from f4df591 to 6e7fa45CompareJuly 10, 2026 23:05
@potiukpotiuk added the ready for maintainer review Set after triaging when all criteria pass. label Jul 11, 2026

@aaron-y-chenaaron-y-chen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, thanks for the PR, very impressive :)

Comment threadproviders/google/src/airflow/providers/google/cloud/operators/dataflow.py Outdated
@steveahnahn
steveahnahnforce-pushed the dataflow-cancel-job-on-user-kill-deferred branch from 6e7fa45 to 54df2f5CompareJuly 16, 2026 02:04
Killing a deferred Dataflow task (mark-failed, clear or mark-succeeded)
leaves the Dataflow job running: the trigger has no on_kill, so the
user-kill cancellation the sibling BigQuery and Dataproc triggers
already perform never happens, and a streaming pipeline keeps running
and billing until someone notices in the Cloud console. Before the
Airflow 3.3 trigger cancellation redesign the legacy path cancelled the
job from inside the trigger, so this is also a behavior regression for
deferrable users.
Implement on_kill on TemplateJobStartTrigger following the merged
BigQuery/Dataproc/EMR pattern, honoring the operator's drain_pipeline
setting so streaming jobs are drained rather than cancelled when
configured, exactly as the non-deferred kill path does. The hook is
built and invoked inside a worker thread because the synchronous
DataflowHook resolves its connection during construction, which must
not run in the triggerer's event loop.
@steveahnahn
steveahnahnforce-pushed the dataflow-cancel-job-on-user-kill-deferred branch from 54df2f5 to bd3f1bbCompareJuly 16, 2026 02:05
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:providersprovider:googleGoogle (including GCP) related issuesready for maintainer reviewSet after triaging when all criteria pass.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@steveahnahn@aaron-y-chen@potiuk
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Cancel the Dataflow job when a user kills the deferred task by steveahnahn · Pull Request #69586 · apache/airflow · GitHub
Skip to content

Cancel the Dataflow job when a user kills the deferred task - #69586

Open
steveahnahn wants to merge 2 commits into
apache:mainfrom
steveahnahn:dataflow-cancel-job-on-user-kill-deferred
Open

Cancel the Dataflow job when a user kills the deferred task#69586
steveahnahn wants to merge 2 commits into
apache:mainfrom
steveahnahn:dataflow-cancel-job-on-user-kill-deferred

Conversation

@steveahnahn

@steveahnahnsteveahnahn commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Problem

  • Killing a deferred Dataflow task (mark failed, clear, mark success) leaves the Dataflow job running and billing in GCP.
  • Neither TemplateJobStartTrigger (templated/flex jobs) nor DataflowStartYamlJobTrigger (YAML jobs) has on_kill; the BigQuery and Dataproc triggers in this provider already cancel in the same situation.
  • The YAML operator's own on_kill even documents the gap: it "will not be called if a task instance is killed in a deferred state."
  • A streaming job runs until someone notices it in the Cloud console.

Change

  • Add on_kill to both TemplateJobStartTrigger and DataflowStartYamlJobTrigger: checks cancel_on_kill, job_id, project_id, then cancels via the sync DataflowHook. Identical cancel path in both.
  • drain_pipeline is honored, matching the non deferred kill path.
  • Hook is constructed and invoked inside a worker thread (sync_to_async): DataflowHook resolves its connection eagerly at construction, which must not run in the triggerer event loop. Construction inside the try means a connection error is logged, not raised.
  • Fires only on user kill, never on triggerer restart, redistribution, or timeout.

End to end verification

  1. Deferrable operator launched the job; task deferred, job JOB_STATE_RUNNING.
deferred task, job running
  1. Marked the task failed from the UI.
marking the deferred task failed
  1. Triggerer log, no exception escaping on_kill:
Trigger cancelled by user action, invoking on_kill
Stopping Dataflow job. Project ID: <redacted>, Location: us-central1, Job ID: 2026-07-08_21_43_34-418737444659630262, drain: False
  1. Job went JOB_STATE_RUNNING -> JOB_STATE_CANCELLING -> JOB_STATE_CANCELLED (gcloud dataflow jobs list). With cancel_on_kill=False the job keeps running.

The YAML trigger uses the identical DataflowHook.cancel_job path proven above, so the e2e run covers its cancel mechanism; it is additionally unit tested.

Tests

  • on_kill tests for both triggers: cancel, drain_pipeline honored, no ops (cancel_on_kill=False, missing job_id/project_id), hook errors swallowed, hook construction errors swallowed; plus operator threading of drain_pipeline into the trigger.
  • All new assertions fail without the change; 95 trigger tests pass with it; provider mypy clean.

Was generative AI tooling used to co-author this PR?
  • Yes, Claude Code (Opus 4.8)

Generated-by: Claude Code (Opus 4.8) following the guidelines

@boring-cyborgboring-cyborgBot added area:providers provider:google Google (including GCP) related issues labels Jul 8, 2026
@steveahnahn
steveahnahnforce-pushed the dataflow-cancel-job-on-user-kill-deferred branch 2 times, most recently from cadba9a to f4df591CompareJuly 8, 2026 06:46
@steveahnahn
steveahnahn marked this pull request as ready for review July 8, 2026 21:02
@steveahnahn
steveahnahn requested a review from shahar1 as a code ownerJuly 8, 2026 21:02
@steveahnahn
steveahnahnforce-pushed the dataflow-cancel-job-on-user-kill-deferred branch from f4df591 to 6e7fa45CompareJuly 10, 2026 23:05
@potiukpotiuk added the ready for maintainer review Set after triaging when all criteria pass. label Jul 11, 2026

@aaron-y-chenaaron-y-chen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, thanks for the PR, very impressive :)

Comment threadproviders/google/src/airflow/providers/google/cloud/operators/dataflow.py Outdated
@steveahnahn
steveahnahnforce-pushed the dataflow-cancel-job-on-user-kill-deferred branch from 6e7fa45 to 54df2f5CompareJuly 16, 2026 02:04
Killing a deferred Dataflow task (mark-failed, clear or mark-succeeded)
leaves the Dataflow job running: the trigger has no on_kill, so the
user-kill cancellation the sibling BigQuery and Dataproc triggers
already perform never happens, and a streaming pipeline keeps running
and billing until someone notices in the Cloud console. Before the
Airflow 3.3 trigger cancellation redesign the legacy path cancelled the
job from inside the trigger, so this is also a behavior regression for
deferrable users.
Implement on_kill on TemplateJobStartTrigger following the merged
BigQuery/Dataproc/EMR pattern, honoring the operator's drain_pipeline
setting so streaming jobs are drained rather than cancelled when
configured, exactly as the non-deferred kill path does. The hook is
built and invoked inside a worker thread because the synchronous
DataflowHook resolves its connection during construction, which must
not run in the triggerer's event loop.
@steveahnahn
steveahnahnforce-pushed the dataflow-cancel-job-on-user-kill-deferred branch from 54df2f5 to bd3f1bbCompareJuly 16, 2026 02:05
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:providersprovider:googleGoogle (including GCP) related issuesready for maintainer reviewSet after triaging when all criteria pass.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@steveahnahn@aaron-y-chen@potiuk
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Cancel the Dataflow job when a user kills the deferred task by steveahnahn · Pull Request #69586 · apache/airflow · GitHub
Skip to content

Cancel the Dataflow job when a user kills the deferred task - #69586

Open
steveahnahn wants to merge 2 commits into
apache:mainfrom
steveahnahn:dataflow-cancel-job-on-user-kill-deferred
Open

Cancel the Dataflow job when a user kills the deferred task#69586
steveahnahn wants to merge 2 commits into
apache:mainfrom
steveahnahn:dataflow-cancel-job-on-user-kill-deferred

Conversation

@steveahnahn

@steveahnahnsteveahnahn commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Problem

  • Killing a deferred Dataflow task (mark failed, clear, mark success) leaves the Dataflow job running and billing in GCP.
  • Neither TemplateJobStartTrigger (templated/flex jobs) nor DataflowStartYamlJobTrigger (YAML jobs) has on_kill; the BigQuery and Dataproc triggers in this provider already cancel in the same situation.
  • The YAML operator's own on_kill even documents the gap: it "will not be called if a task instance is killed in a deferred state."
  • A streaming job runs until someone notices it in the Cloud console.

Change

  • Add on_kill to both TemplateJobStartTrigger and DataflowStartYamlJobTrigger: checks cancel_on_kill, job_id, project_id, then cancels via the sync DataflowHook. Identical cancel path in both.
  • drain_pipeline is honored, matching the non deferred kill path.
  • Hook is constructed and invoked inside a worker thread (sync_to_async): DataflowHook resolves its connection eagerly at construction, which must not run in the triggerer event loop. Construction inside the try means a connection error is logged, not raised.
  • Fires only on user kill, never on triggerer restart, redistribution, or timeout.

End to end verification

  1. Deferrable operator launched the job; task deferred, job JOB_STATE_RUNNING.
deferred task, job running
  1. Marked the task failed from the UI.
marking the deferred task failed
  1. Triggerer log, no exception escaping on_kill:
Trigger cancelled by user action, invoking on_kill
Stopping Dataflow job. Project ID: <redacted>, Location: us-central1, Job ID: 2026-07-08_21_43_34-418737444659630262, drain: False
  1. Job went JOB_STATE_RUNNING -> JOB_STATE_CANCELLING -> JOB_STATE_CANCELLED (gcloud dataflow jobs list). With cancel_on_kill=False the job keeps running.

The YAML trigger uses the identical DataflowHook.cancel_job path proven above, so the e2e run covers its cancel mechanism; it is additionally unit tested.

Tests

  • on_kill tests for both triggers: cancel, drain_pipeline honored, no ops (cancel_on_kill=False, missing job_id/project_id), hook errors swallowed, hook construction errors swallowed; plus operator threading of drain_pipeline into the trigger.
  • All new assertions fail without the change; 95 trigger tests pass with it; provider mypy clean.

Was generative AI tooling used to co-author this PR?
  • Yes, Claude Code (Opus 4.8)

Generated-by: Claude Code (Opus 4.8) following the guidelines

@boring-cyborgboring-cyborgBot added area:providers provider:google Google (including GCP) related issues labels Jul 8, 2026
@steveahnahn
steveahnahnforce-pushed the dataflow-cancel-job-on-user-kill-deferred branch 2 times, most recently from cadba9a to f4df591CompareJuly 8, 2026 06:46
@steveahnahn
steveahnahn marked this pull request as ready for review July 8, 2026 21:02
@steveahnahn
steveahnahn requested a review from shahar1 as a code ownerJuly 8, 2026 21:02
@steveahnahn
steveahnahnforce-pushed the dataflow-cancel-job-on-user-kill-deferred branch from f4df591 to 6e7fa45CompareJuly 10, 2026 23:05
@potiukpotiuk added the ready for maintainer review Set after triaging when all criteria pass. label Jul 11, 2026

@aaron-y-chenaaron-y-chen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, thanks for the PR, very impressive :)

Comment threadproviders/google/src/airflow/providers/google/cloud/operators/dataflow.py Outdated
@steveahnahn
steveahnahnforce-pushed the dataflow-cancel-job-on-user-kill-deferred branch from 6e7fa45 to 54df2f5CompareJuly 16, 2026 02:04
Killing a deferred Dataflow task (mark-failed, clear or mark-succeeded)
leaves the Dataflow job running: the trigger has no on_kill, so the
user-kill cancellation the sibling BigQuery and Dataproc triggers
already perform never happens, and a streaming pipeline keeps running
and billing until someone notices in the Cloud console. Before the
Airflow 3.3 trigger cancellation redesign the legacy path cancelled the
job from inside the trigger, so this is also a behavior regression for
deferrable users.
Implement on_kill on TemplateJobStartTrigger following the merged
BigQuery/Dataproc/EMR pattern, honoring the operator's drain_pipeline
setting so streaming jobs are drained rather than cancelled when
configured, exactly as the non-deferred kill path does. The hook is
built and invoked inside a worker thread because the synchronous
DataflowHook resolves its connection during construction, which must
not run in the triggerer's event loop.
@steveahnahn
steveahnahnforce-pushed the dataflow-cancel-job-on-user-kill-deferred branch from 54df2f5 to bd3f1bbCompareJuly 16, 2026 02:05
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:providersprovider:googleGoogle (including GCP) related issuesready for maintainer reviewSet after triaging when all criteria pass.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@steveahnahn@aaron-y-chen@potiuk
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' Cancel the Dataflow job when a user kills the deferred task by steveahnahn · Pull Request #69586 · apache/airflow · GitHub
Skip to content

Cancel the Dataflow job when a user kills the deferred task - #69586

Open
steveahnahn wants to merge 2 commits into
apache:mainfrom
steveahnahn:dataflow-cancel-job-on-user-kill-deferred
Open

Cancel the Dataflow job when a user kills the deferred task#69586
steveahnahn wants to merge 2 commits into
apache:mainfrom
steveahnahn:dataflow-cancel-job-on-user-kill-deferred

Conversation

@steveahnahn

@steveahnahnsteveahnahn commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Problem

  • Killing a deferred Dataflow task (mark failed, clear, mark success) leaves the Dataflow job running and billing in GCP.
  • Neither TemplateJobStartTrigger (templated/flex jobs) nor DataflowStartYamlJobTrigger (YAML jobs) has on_kill; the BigQuery and Dataproc triggers in this provider already cancel in the same situation.
  • The YAML operator's own on_kill even documents the gap: it "will not be called if a task instance is killed in a deferred state."
  • A streaming job runs until someone notices it in the Cloud console.

Change

  • Add on_kill to both TemplateJobStartTrigger and DataflowStartYamlJobTrigger: checks cancel_on_kill, job_id, project_id, then cancels via the sync DataflowHook. Identical cancel path in both.
  • drain_pipeline is honored, matching the non deferred kill path.
  • Hook is constructed and invoked inside a worker thread (sync_to_async): DataflowHook resolves its connection eagerly at construction, which must not run in the triggerer event loop. Construction inside the try means a connection error is logged, not raised.
  • Fires only on user kill, never on triggerer restart, redistribution, or timeout.

End to end verification

  1. Deferrable operator launched the job; task deferred, job JOB_STATE_RUNNING.
deferred task, job running
  1. Marked the task failed from the UI.
marking the deferred task failed
  1. Triggerer log, no exception escaping on_kill:
Trigger cancelled by user action, invoking on_kill
Stopping Dataflow job. Project ID: <redacted>, Location: us-central1, Job ID: 2026-07-08_21_43_34-418737444659630262, drain: False
  1. Job went JOB_STATE_RUNNING -> JOB_STATE_CANCELLING -> JOB_STATE_CANCELLED (gcloud dataflow jobs list). With cancel_on_kill=False the job keeps running.

The YAML trigger uses the identical DataflowHook.cancel_job path proven above, so the e2e run covers its cancel mechanism; it is additionally unit tested.

Tests

  • on_kill tests for both triggers: cancel, drain_pipeline honored, no ops (cancel_on_kill=False, missing job_id/project_id), hook errors swallowed, hook construction errors swallowed; plus operator threading of drain_pipeline into the trigger.
  • All new assertions fail without the change; 95 trigger tests pass with it; provider mypy clean.

Was generative AI tooling used to co-author this PR?
  • Yes, Claude Code (Opus 4.8)

Generated-by: Claude Code (Opus 4.8) following the guidelines

@boring-cyborgboring-cyborgBot added area:providers provider:google Google (including GCP) related issues labels Jul 8, 2026
@steveahnahn
steveahnahnforce-pushed the dataflow-cancel-job-on-user-kill-deferred branch 2 times, most recently from cadba9a to f4df591CompareJuly 8, 2026 06:46
@steveahnahn
steveahnahn marked this pull request as ready for review July 8, 2026 21:02
@steveahnahn
steveahnahn requested a review from shahar1 as a code ownerJuly 8, 2026 21:02
@steveahnahn
steveahnahnforce-pushed the dataflow-cancel-job-on-user-kill-deferred branch from f4df591 to 6e7fa45CompareJuly 10, 2026 23:05
@potiukpotiuk added the ready for maintainer review Set after triaging when all criteria pass. label Jul 11, 2026

@aaron-y-chenaaron-y-chen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, thanks for the PR, very impressive :)

Comment threadproviders/google/src/airflow/providers/google/cloud/operators/dataflow.py Outdated
@steveahnahn
steveahnahnforce-pushed the dataflow-cancel-job-on-user-kill-deferred branch from 6e7fa45 to 54df2f5CompareJuly 16, 2026 02:04
Killing a deferred Dataflow task (mark-failed, clear or mark-succeeded)
leaves the Dataflow job running: the trigger has no on_kill, so the
user-kill cancellation the sibling BigQuery and Dataproc triggers
already perform never happens, and a streaming pipeline keeps running
and billing until someone notices in the Cloud console. Before the
Airflow 3.3 trigger cancellation redesign the legacy path cancelled the
job from inside the trigger, so this is also a behavior regression for
deferrable users.
Implement on_kill on TemplateJobStartTrigger following the merged
BigQuery/Dataproc/EMR pattern, honoring the operator's drain_pipeline
setting so streaming jobs are drained rather than cancelled when
configured, exactly as the non-deferred kill path does. The hook is
built and invoked inside a worker thread because the synchronous
DataflowHook resolves its connection during construction, which must
not run in the triggerer's event loop.
@steveahnahn
steveahnahnforce-pushed the dataflow-cancel-job-on-user-kill-deferred branch from 54df2f5 to bd3f1bbCompareJuly 16, 2026 02:05
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:providersprovider:googleGoogle (including GCP) related issuesready for maintainer reviewSet after triaging when all criteria pass.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@steveahnahn@aaron-y-chen@potiuk
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Cancel the Dataflow job when a user kills the deferred task by steveahnahn · Pull Request #69586 · apache/airflow · GitHub
Skip to content

Cancel the Dataflow job when a user kills the deferred task - #69586

Open
steveahnahn wants to merge 2 commits into
apache:mainfrom
steveahnahn:dataflow-cancel-job-on-user-kill-deferred
Open

Cancel the Dataflow job when a user kills the deferred task#69586
steveahnahn wants to merge 2 commits into
apache:mainfrom
steveahnahn:dataflow-cancel-job-on-user-kill-deferred

Conversation

@steveahnahn

@steveahnahnsteveahnahn commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Problem

  • Killing a deferred Dataflow task (mark failed, clear, mark success) leaves the Dataflow job running and billing in GCP.
  • Neither TemplateJobStartTrigger (templated/flex jobs) nor DataflowStartYamlJobTrigger (YAML jobs) has on_kill; the BigQuery and Dataproc triggers in this provider already cancel in the same situation.
  • The YAML operator's own on_kill even documents the gap: it "will not be called if a task instance is killed in a deferred state."
  • A streaming job runs until someone notices it in the Cloud console.

Change

  • Add on_kill to both TemplateJobStartTrigger and DataflowStartYamlJobTrigger: checks cancel_on_kill, job_id, project_id, then cancels via the sync DataflowHook. Identical cancel path in both.
  • drain_pipeline is honored, matching the non deferred kill path.
  • Hook is constructed and invoked inside a worker thread (sync_to_async): DataflowHook resolves its connection eagerly at construction, which must not run in the triggerer event loop. Construction inside the try means a connection error is logged, not raised.
  • Fires only on user kill, never on triggerer restart, redistribution, or timeout.

End to end verification

  1. Deferrable operator launched the job; task deferred, job JOB_STATE_RUNNING.
deferred task, job running
  1. Marked the task failed from the UI.
marking the deferred task failed
  1. Triggerer log, no exception escaping on_kill:
Trigger cancelled by user action, invoking on_kill
Stopping Dataflow job. Project ID: <redacted>, Location: us-central1, Job ID: 2026-07-08_21_43_34-418737444659630262, drain: False
  1. Job went JOB_STATE_RUNNING -> JOB_STATE_CANCELLING -> JOB_STATE_CANCELLED (gcloud dataflow jobs list). With cancel_on_kill=False the job keeps running.

The YAML trigger uses the identical DataflowHook.cancel_job path proven above, so the e2e run covers its cancel mechanism; it is additionally unit tested.

Tests

  • on_kill tests for both triggers: cancel, drain_pipeline honored, no ops (cancel_on_kill=False, missing job_id/project_id), hook errors swallowed, hook construction errors swallowed; plus operator threading of drain_pipeline into the trigger.
  • All new assertions fail without the change; 95 trigger tests pass with it; provider mypy clean.

Was generative AI tooling used to co-author this PR?
  • Yes, Claude Code (Opus 4.8)

Generated-by: Claude Code (Opus 4.8) following the guidelines

@boring-cyborgboring-cyborgBot added area:providers provider:google Google (including GCP) related issues labels Jul 8, 2026
@steveahnahn
steveahnahnforce-pushed the dataflow-cancel-job-on-user-kill-deferred branch 2 times, most recently from cadba9a to f4df591CompareJuly 8, 2026 06:46
@steveahnahn
steveahnahn marked this pull request as ready for review July 8, 2026 21:02
@steveahnahn
steveahnahn requested a review from shahar1 as a code ownerJuly 8, 2026 21:02
@steveahnahn
steveahnahnforce-pushed the dataflow-cancel-job-on-user-kill-deferred branch from f4df591 to 6e7fa45CompareJuly 10, 2026 23:05
@potiukpotiuk added the ready for maintainer review Set after triaging when all criteria pass. label Jul 11, 2026

@aaron-y-chenaaron-y-chen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, thanks for the PR, very impressive :)

Comment threadproviders/google/src/airflow/providers/google/cloud/operators/dataflow.py Outdated
@steveahnahn
steveahnahnforce-pushed the dataflow-cancel-job-on-user-kill-deferred branch from 6e7fa45 to 54df2f5CompareJuly 16, 2026 02:04
Killing a deferred Dataflow task (mark-failed, clear or mark-succeeded)
leaves the Dataflow job running: the trigger has no on_kill, so the
user-kill cancellation the sibling BigQuery and Dataproc triggers
already perform never happens, and a streaming pipeline keeps running
and billing until someone notices in the Cloud console. Before the
Airflow 3.3 trigger cancellation redesign the legacy path cancelled the
job from inside the trigger, so this is also a behavior regression for
deferrable users.
Implement on_kill on TemplateJobStartTrigger following the merged
BigQuery/Dataproc/EMR pattern, honoring the operator's drain_pipeline
setting so streaming jobs are drained rather than cancelled when
configured, exactly as the non-deferred kill path does. The hook is
built and invoked inside a worker thread because the synchronous
DataflowHook resolves its connection during construction, which must
not run in the triggerer's event loop.
@steveahnahn
steveahnahnforce-pushed the dataflow-cancel-job-on-user-kill-deferred branch from 54df2f5 to bd3f1bbCompareJuly 16, 2026 02:05
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:providersprovider:googleGoogle (including GCP) related issuesready for maintainer reviewSet after triaging when all criteria pass.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@steveahnahn@aaron-y-chen@potiuk
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Cancel the Dataflow job when a user kills the deferred task by steveahnahn · Pull Request #69586 · apache/airflow · GitHub
Skip to content

Cancel the Dataflow job when a user kills the deferred task - #69586

Open
steveahnahn wants to merge 2 commits into
apache:mainfrom
steveahnahn:dataflow-cancel-job-on-user-kill-deferred
Open

Cancel the Dataflow job when a user kills the deferred task#69586
steveahnahn wants to merge 2 commits into
apache:mainfrom
steveahnahn:dataflow-cancel-job-on-user-kill-deferred

Conversation

@steveahnahn

@steveahnahnsteveahnahn commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Problem

  • Killing a deferred Dataflow task (mark failed, clear, mark success) leaves the Dataflow job running and billing in GCP.
  • Neither TemplateJobStartTrigger (templated/flex jobs) nor DataflowStartYamlJobTrigger (YAML jobs) has on_kill; the BigQuery and Dataproc triggers in this provider already cancel in the same situation.
  • The YAML operator's own on_kill even documents the gap: it "will not be called if a task instance is killed in a deferred state."
  • A streaming job runs until someone notices it in the Cloud console.

Change

  • Add on_kill to both TemplateJobStartTrigger and DataflowStartYamlJobTrigger: checks cancel_on_kill, job_id, project_id, then cancels via the sync DataflowHook. Identical cancel path in both.
  • drain_pipeline is honored, matching the non deferred kill path.
  • Hook is constructed and invoked inside a worker thread (sync_to_async): DataflowHook resolves its connection eagerly at construction, which must not run in the triggerer event loop. Construction inside the try means a connection error is logged, not raised.
  • Fires only on user kill, never on triggerer restart, redistribution, or timeout.

End to end verification

  1. Deferrable operator launched the job; task deferred, job JOB_STATE_RUNNING.
deferred task, job running
  1. Marked the task failed from the UI.
marking the deferred task failed
  1. Triggerer log, no exception escaping on_kill:
Trigger cancelled by user action, invoking on_kill
Stopping Dataflow job. Project ID: <redacted>, Location: us-central1, Job ID: 2026-07-08_21_43_34-418737444659630262, drain: False
  1. Job went JOB_STATE_RUNNING -> JOB_STATE_CANCELLING -> JOB_STATE_CANCELLED (gcloud dataflow jobs list). With cancel_on_kill=False the job keeps running.

The YAML trigger uses the identical DataflowHook.cancel_job path proven above, so the e2e run covers its cancel mechanism; it is additionally unit tested.

Tests

  • on_kill tests for both triggers: cancel, drain_pipeline honored, no ops (cancel_on_kill=False, missing job_id/project_id), hook errors swallowed, hook construction errors swallowed; plus operator threading of drain_pipeline into the trigger.
  • All new assertions fail without the change; 95 trigger tests pass with it; provider mypy clean.

Was generative AI tooling used to co-author this PR?
  • Yes, Claude Code (Opus 4.8)

Generated-by: Claude Code (Opus 4.8) following the guidelines

@boring-cyborgboring-cyborgBot added area:providers provider:google Google (including GCP) related issues labels Jul 8, 2026
@steveahnahn
steveahnahnforce-pushed the dataflow-cancel-job-on-user-kill-deferred branch 2 times, most recently from cadba9a to f4df591CompareJuly 8, 2026 06:46
@steveahnahn
steveahnahn marked this pull request as ready for review July 8, 2026 21:02
@steveahnahn
steveahnahn requested a review from shahar1 as a code ownerJuly 8, 2026 21:02
@steveahnahn
steveahnahnforce-pushed the dataflow-cancel-job-on-user-kill-deferred branch from f4df591 to 6e7fa45CompareJuly 10, 2026 23:05
@potiukpotiuk added the ready for maintainer review Set after triaging when all criteria pass. label Jul 11, 2026

@aaron-y-chenaaron-y-chen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, thanks for the PR, very impressive :)

Comment threadproviders/google/src/airflow/providers/google/cloud/operators/dataflow.py Outdated
@steveahnahn
steveahnahnforce-pushed the dataflow-cancel-job-on-user-kill-deferred branch from 6e7fa45 to 54df2f5CompareJuly 16, 2026 02:04
Killing a deferred Dataflow task (mark-failed, clear or mark-succeeded)
leaves the Dataflow job running: the trigger has no on_kill, so the
user-kill cancellation the sibling BigQuery and Dataproc triggers
already perform never happens, and a streaming pipeline keeps running
and billing until someone notices in the Cloud console. Before the
Airflow 3.3 trigger cancellation redesign the legacy path cancelled the
job from inside the trigger, so this is also a behavior regression for
deferrable users.
Implement on_kill on TemplateJobStartTrigger following the merged
BigQuery/Dataproc/EMR pattern, honoring the operator's drain_pipeline
setting so streaming jobs are drained rather than cancelled when
configured, exactly as the non-deferred kill path does. The hook is
built and invoked inside a worker thread because the synchronous
DataflowHook resolves its connection during construction, which must
not run in the triggerer's event loop.
@steveahnahn
steveahnahnforce-pushed the dataflow-cancel-job-on-user-kill-deferred branch from 54df2f5 to bd3f1bbCompareJuly 16, 2026 02:05
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:providersprovider:googleGoogle (including GCP) related issuesready for maintainer reviewSet after triaging when all criteria pass.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@steveahnahn@aaron-y-chen@potiuk
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); Cancel the Dataflow job when a user kills the deferred task by steveahnahn · Pull Request #69586 · apache/airflow · GitHub
Skip to content

Cancel the Dataflow job when a user kills the deferred task - #69586

Open
steveahnahn wants to merge 2 commits into
apache:mainfrom
steveahnahn:dataflow-cancel-job-on-user-kill-deferred
Open

Cancel the Dataflow job when a user kills the deferred task#69586
steveahnahn wants to merge 2 commits into
apache:mainfrom
steveahnahn:dataflow-cancel-job-on-user-kill-deferred

Conversation

@steveahnahn

@steveahnahnsteveahnahn commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Problem

  • Killing a deferred Dataflow task (mark failed, clear, mark success) leaves the Dataflow job running and billing in GCP.
  • Neither TemplateJobStartTrigger (templated/flex jobs) nor DataflowStartYamlJobTrigger (YAML jobs) has on_kill; the BigQuery and Dataproc triggers in this provider already cancel in the same situation.
  • The YAML operator's own on_kill even documents the gap: it "will not be called if a task instance is killed in a deferred state."
  • A streaming job runs until someone notices it in the Cloud console.

Change

  • Add on_kill to both TemplateJobStartTrigger and DataflowStartYamlJobTrigger: checks cancel_on_kill, job_id, project_id, then cancels via the sync DataflowHook. Identical cancel path in both.
  • drain_pipeline is honored, matching the non deferred kill path.
  • Hook is constructed and invoked inside a worker thread (sync_to_async): DataflowHook resolves its connection eagerly at construction, which must not run in the triggerer event loop. Construction inside the try means a connection error is logged, not raised.
  • Fires only on user kill, never on triggerer restart, redistribution, or timeout.

End to end verification

  1. Deferrable operator launched the job; task deferred, job JOB_STATE_RUNNING.
deferred task, job running
  1. Marked the task failed from the UI.
marking the deferred task failed
  1. Triggerer log, no exception escaping on_kill:
Trigger cancelled by user action, invoking on_kill
Stopping Dataflow job. Project ID: <redacted>, Location: us-central1, Job ID: 2026-07-08_21_43_34-418737444659630262, drain: False
  1. Job went JOB_STATE_RUNNING -> JOB_STATE_CANCELLING -> JOB_STATE_CANCELLED (gcloud dataflow jobs list). With cancel_on_kill=False the job keeps running.

The YAML trigger uses the identical DataflowHook.cancel_job path proven above, so the e2e run covers its cancel mechanism; it is additionally unit tested.

Tests

  • on_kill tests for both triggers: cancel, drain_pipeline honored, no ops (cancel_on_kill=False, missing job_id/project_id), hook errors swallowed, hook construction errors swallowed; plus operator threading of drain_pipeline into the trigger.
  • All new assertions fail without the change; 95 trigger tests pass with it; provider mypy clean.

Was generative AI tooling used to co-author this PR?
  • Yes, Claude Code (Opus 4.8)

Generated-by: Claude Code (Opus 4.8) following the guidelines

@boring-cyborgboring-cyborgBot added area:providers provider:google Google (including GCP) related issues labels Jul 8, 2026
@steveahnahn
steveahnahnforce-pushed the dataflow-cancel-job-on-user-kill-deferred branch 2 times, most recently from cadba9a to f4df591CompareJuly 8, 2026 06:46
@steveahnahn
steveahnahn marked this pull request as ready for review July 8, 2026 21:02
@steveahnahn
steveahnahn requested a review from shahar1 as a code ownerJuly 8, 2026 21:02
@steveahnahn
steveahnahnforce-pushed the dataflow-cancel-job-on-user-kill-deferred branch from f4df591 to 6e7fa45CompareJuly 10, 2026 23:05
@potiukpotiuk added the ready for maintainer review Set after triaging when all criteria pass. label Jul 11, 2026

@aaron-y-chenaaron-y-chen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, thanks for the PR, very impressive :)

Comment threadproviders/google/src/airflow/providers/google/cloud/operators/dataflow.py Outdated
@steveahnahn
steveahnahnforce-pushed the dataflow-cancel-job-on-user-kill-deferred branch from 6e7fa45 to 54df2f5CompareJuly 16, 2026 02:04
Killing a deferred Dataflow task (mark-failed, clear or mark-succeeded)
leaves the Dataflow job running: the trigger has no on_kill, so the
user-kill cancellation the sibling BigQuery and Dataproc triggers
already perform never happens, and a streaming pipeline keeps running
and billing until someone notices in the Cloud console. Before the
Airflow 3.3 trigger cancellation redesign the legacy path cancelled the
job from inside the trigger, so this is also a behavior regression for
deferrable users.
Implement on_kill on TemplateJobStartTrigger following the merged
BigQuery/Dataproc/EMR pattern, honoring the operator's drain_pipeline
setting so streaming jobs are drained rather than cancelled when
configured, exactly as the non-deferred kill path does. The hook is
built and invoked inside a worker thread because the synchronous
DataflowHook resolves its connection during construction, which must
not run in the triggerer's event loop.
@steveahnahn
steveahnahnforce-pushed the dataflow-cancel-job-on-user-kill-deferred branch from 54df2f5 to bd3f1bbCompareJuly 16, 2026 02:05
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:providersprovider:googleGoogle (including GCP) related issuesready for maintainer reviewSet after triaging when all criteria pass.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@steveahnahn@aaron-y-chen@potiuk