Conversation
…ons on teardown TWD teardown force-deletes every worker deployment version with SkipDrainage: true, which bypasses the one server-side guard against deleting a version that still has open pinned executions. Those workflows are then stranded permanently - their tasks route by exact version match and a task on a base task queue has no schedule-to-start timeout, so once the version is gone nothing dispatches them and nothing expires them. Today this happens with no record of which workflows were lost. Query visibility for open executions pinned to each version immediately before the force-delete, then log the count, a sample of workflow IDs, and the query itself, and emit a Warning Event. The delete still proceeds: blocking teardown would hold the TWD in Terminating for as long as the longest pinned workflow runs, trading a data-integrity failure for an availability one. This converts a silent permanent failure into a recoverable one with a list to work from. The log line is the durable record, not the Event or a status condition. This runs inside the finalizer, so the TWD is deleted moments later and any condition written here goes with it; cluster Events expire on the apiserver's event-ttl. Controller logs are already scraped and are where the ARUN-1232 / DISTR-947 detection queries look. A visibility failure never blocks teardown - it reports that the check could not run and proceeds. The steady-state reaper in planner.go is unchanged: it only deletes Drained versions, which by definition have no open pinned executions. Refs ARUN-1232. The trigger that caused the accidental teardown on 2026-08-31 is tracked separately in DISTR-947. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
TWD teardown can permanently strand in-flight customer workflows, and today it leaves no record of which ones. This makes it leave one.
handleDeletionforce-deletes every worker deployment version withSkipDrainage: true. That flag bypasses the one server-side guard Temporal provides against deleting a version that still has open pinned executions. Those workflows are then unrecoverable without manual intervention: their tasks route by exact deployment-version match, and a task on a base (non-sticky) task queue has no schedule-to-start timeout, so once the version is gone nothing dispatches them and nothing expires them. They sit atattempt: 1forever.This fired on 2026-08-31 and stranded six customer workflows. One had been stranded since 08-27 and nobody noticed, because there was nothing to notice.
What this changes
Before each force-delete, query visibility for open executions pinned to that version:
If the count is non-zero, log the count, a sample of workflow IDs, and the query itself, then emit a Warning Event (
PinnedExecutionsStranded).The delete still proceeds. Honouring drainage here would hold the TWD in
Terminatingfor as long as the longest pinned workflow runs - a 72-hour Snowflake crawl blocks the namespace and any reinstall for 72 hours. That trades a silent data-integrity failure for a loud availability failure. This PR does not make that trade; it only stops the failure being silent. Bounded drainage is ARUN-1232 item 2 and is deliberately out of scope here.Why the log line, not a status condition
ARUN-1232 proposes setting a status condition. That does not work on this path and the ticket should be read with this correction:
event-ttl, commonly 1h. Against an incident that went unnoticed for 20 hours and 5 days, that is weak.otel_logs.filelogs_deploymentand are exactly where the DISTR-947 detection queries look.So the log line is the contract. The Event is the human-facing nudge. The status condition belongs to item 2, where the object stays around to carry it.
Grep target for detection:
Deliberate choices
PinnedExecutionCheckFailed) and returns. Proceeding keeps teardown reliable at the cost of reintroducing the silent case exactly when Temporal is unhealthy - the alternative risks wedging teardown on an unrelated outage, which is the failure mode ARUN-1182 is already about.planner.gois untouched. The steady-state reaper only deletesVersionStatusDrained, which by definition has no open pinned executions. Adding the check there would be cost with no signal.TemporalWorkflowVersioningBehavior="Pinned". AutoUpgrade workflows on a doomed version are not stranded and will inflate the count, but the extra built-in search attribute may not be registered on every namespace, and a failed query degrades to the check-failed path. Over-reporting on a warning is the safer side to err on.Testing
go test ./internal/controller/...- 137 pass. Four new tests inteardown_pinned_test.gocover: open pinned executions logged and evented with IDs; nothing pinned stays silent and skips the list call; count failure proceeds with a warning; list failure still reports the count.handleDeletionitself is not unit-testable - it dials the client pool directly - so "the delete still proceeds" is guaranteed structurally byrecordPinnedExecutionshaving no return value, not by a test.make lint-codereports one pre-existing finding,execplan.go:355cyclomatic complexity, identical on the base branch and in an untouched file.Scope
No CRD change, no API change, no new flag, no behaviour change to the delete itself. Should rebase cleanly and is a reasonable upstream candidate.
Refs ARUN-1232. The trigger for the accidental teardown is tracked separately in DISTR-947 (Flux
remediation.strategy: uninstall), which is still necessary - this PR does not remove the need for it.🤖 Generated with Claude Code