Skip to content

Make RedshiftDeleteClusterOperator delete reliably during cluster transitions#69574

Open
seanghaeli wants to merge 4 commits into
apache:mainfrom
aws-mwaa:feature/redshift-delete-deferrable-wait
Open

Make RedshiftDeleteClusterOperator delete reliably during cluster transitions#69574
seanghaeli wants to merge 4 commits into
apache:mainfrom
aws-mwaa:feature/redshift-delete-deferrable-wait

Conversation

@seanghaeli

@seanghaeli seanghaeli commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Why

RedshiftDeleteClusterOperator retries on InvalidClusterStateFault when the cluster is mid-transition (pausing/resuming/resizing), but the retry budget was hardcoded to 10 attempts * 15s = 2.5 minutes. Transitions routinely take longer, so the loop exhausts and re-raises before the cluster becomes deletable.

The error seen mid-transition:

InvalidClusterState: Unable to delete the cluster ... You can only delete clusters with
ACTIVE, INCOMPATIBLE_HSM, INSUFFICIENT_VPCE, HARDWARE_FAILURE, INCOMPATIBLE_NETWORK,
PAUSED, INACCESSIBLE_KMS_KEY, INSUFFICIENT_CAPACITY, INCOMPATIBLE_RESTORE lifecycles.

What

Non-deferrable mode: raise the synchronous busy-retry budget to 60 * 15s = ~15 min so it outlasts a transition. Still throw failure once timed out.

Deferrable mode: previously the synchronous retry loop ran before the defer, blocking the worker for the whole budget (defeating the purpose of deferrable mode). Now the operator attempts the delete once; on InvalidClusterStateFault it defers to a new RedshiftClusterSettledTrigger that treats transitional states as retry and fires once the cluster reaches a deletable lifecycle. A callback then re-issues the delete and defers to the existing RedshiftDeleteClusterTrigger (cluster_deleted); on a race it re-defers to the settle trigger. No worker is blocked at any point.

Mirrors the deferrable re-defer pattern established for EKS in #32355

Testing

End-to-end before/after

Non-deferrable — BEFORE: FAILS, cluster leaks:

20:32:04  Cluster in resizing state, unable to delete. 9 attempts remaining.
   ... (15s apart, 9→1) ...
20:34:22  botocore.errorfactory.InvalidClusterStateFault: Unable to delete the cluster rs-e2e-sync ...
          DagRun ... state=failed   TASK_EXIT=1

Non-deferrable — AFTER: SUCCEEDS, cluster deleted:

20:58:33  Cluster in resizing state, unable to delete. 49 attempts remaining.   <-- past stock's 10-cap
21:01:07  Cluster in resizing state, unable to delete. 39 attempts remaining.
21:01:22  new_state=success   DagRun ... state=success   TASK_EXIT=0

Deferrable — BEFORE: blocks worker 2.5 min then FAILS (never defers):

20:51:53  Cluster in resizing state, unable to delete. 9 attempts remaining.
   ... 9→1 ...
20:54:11  new_state=failed   DagRun ... state=failed   TASK_EXIT=1

Deferrable — AFTER: async wait via cluster_deletable waiter, then delete. SUCCEEDS:

21:02:10  Cluster rs-e2e-async is busy; deferring until it settles into a deletable state.
21:02:10  Pausing task as DEFERRED.
21:02:11  Waiting for redshift cluster to settle into a deletable state: ['resizing']
   ... waiter treats 'resizing' as retry for ~10 min, task stays DEFERRED (worker not blocked) ...
21:12:38  (settled) delete re-issued and accepted -> defer to cluster_deleted
21:15:10  Redshift Cluster deletion in progress: ['deleting']
21:16:10  Cluster deleted successfully   DagRun ... state=success   TASK_EXIT=0

@boring-cyborg boring-cyborg Bot added area:providers provider:amazon AWS/Amazon - related issues labels Jul 7, 2026
@seanghaeli seanghaeli marked this pull request as ready for review July 7, 2026 21:31
@seanghaeli seanghaeli requested a review from o-nikolas as a code owner July 7, 2026 21:31
@seanghaeli seanghaeli force-pushed the feature/redshift-delete-deferrable-wait branch from 0f0e718 to f442c94 Compare July 7, 2026 23:30
Sean Ghaeli added 4 commits July 8, 2026 06:43
…nsitions

A delete issued while the cluster is mid-transition (pausing/resuming/resizing)
raises InvalidClusterStateFault. The retry budget was hardcoded to 10 * 15s =
2.5 min, which expires long before a real transition settles (~minutes), so the
task fails and the cluster leaks.

Non-deferrable mode: raise the synchronous busy-retry budget to 60 * 15s = ~15
min so it outlasts a transition; still fail-loud once exhausted.

Deferrable mode: previously the synchronous loop ran before the defer, blocking
the worker. Now attempt the delete once; on InvalidClusterStateFault defer to a
new RedshiftClusterSettledTrigger (an AwsBaseWaiterTrigger backed by a custom
cluster_deletable waiter that treats transitional states as retry and fires once
the cluster is deletable), then a callback re-issues the delete and defers to the
existing RedshiftDeleteClusterTrigger; re-defers on a race. Bounded by the
existing poll_interval/max_attempts. This mirrors the EKS deferrable re-defer
pattern and the AwsBaseWaiterTrigger convention used by the other Redshift
triggers. No worker is blocked in deferrable mode.

Generated-by: Claude Code (Opus)
Comment-only: tighten the RedshiftClusterSettledTrigger class docstring and the
_delete_or_defer_until_settled docstring, keeping the non-obvious rationale
(multi-terminal-state -> custom waiter, which trigger handles which case).

Generated-by: Claude Code (Opus)
Generated-by: Claude Code (Opus)
@seanghaeli seanghaeli force-pushed the feature/redshift-delete-deferrable-wait branch from f442c94 to 10122f7 Compare July 8, 2026 06:46
if validated_event["status"] != "success":
raise AirflowException(f"Error waiting for cluster to become deletable: {validated_event}")

self._delete_or_defer_until_settled()

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.

This might not work if _retry_delete_when_settled is executed on a different machine/host than execute. If some random values are passed to the construct of RedshiftDeleteClusterOperator, then some values will differ between execute and _retry_delete_when_settled. For example, if a random number is used to define cluster_identifier, then definitely this will fail

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I believe the exact same pattern is used here in EKS:

self.hook.delete_cluster(name=self.cluster_name)

I am going to investigate if this causes a fail but I think if this is an issue in this PR, it's also a latent issue in EKS currently

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:providers provider:amazon AWS/Amazon - related issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants