Repository navigation
Conversation
A worker on the build before 0247 executes an agent task or a sub-workflow child from the public column alone. Once that column holds the masked copy, such a worker launches the agent with "[REDACTED]" for the goal's secret and starts the child with "[REDACTED]" inputs. That is what any rolling worker deploy (k8s RollingUpdate included) or any worker rollback runs into, and nothing on the previous build can tell. Every job server of this build now lists a capability marker, cap-sealed-payload-v1, among its Hangfire queues. Nothing is ever enqueued to it; Hangfire's server table carries it to every peer, and a previous build never lists it. The sealer asks the fleet gate before it seals a payload for a new row. While any server with a heartbeat in the last two minutes lacks the marker, or no server is live at all, the payload is written verbatim and unsealed: the row the previous build wrote and the only one it executes correctly. The gate reads the server table at most once per 30 seconds, and an unreadable table answers no. A host with no job storage (a test host) observes no fleet and holds nothing back. A replay or rerun fork is such a new row, staged on the API pod, but it copied the original's ciphertext verbatim. A fork of a sealed child stayed sealed whatever the fleet held, and a previous-version worker ran it with the mask. The fork now opens the original and seals the copy through the gate. Since the API pod asks too, a fleet with no live worker answers no: in a worker rollback's gap between stopping this build's workers and starting the previous ones, the next worker to join may be either build. A row a current worker is already running keeps its shape. The executor re-persists an auto run's resolved model onto its Running row; written through the gate, that unsealed the row, and a later re-attach rebuilt the launch's redactor without the task's workflow secrets, failed the handle's fingerprint and lost the run's tail. No previous-version worker launches a Running row, so that write now seals regardless of the gate. Rows sealed before a previous-version worker joins still need opening, so UnsealNonTerminalPayloadsCommand (POST api/admin/sealed-payloads/unseal, global admin) restores every non-terminal sealed run payload and every queued sealed agent task. A running agent task stays sealed: no worker launches it again, and a current worker's re-attach needs the sealed copy. Each row is a compare-and-swap on its ciphertext. A restored first attempt of a workflow agent with an acceptance contract is re-staked from the restored task, because the composer re-derives the unit's hash from task_jsonb and a masked stake would no longer match. Send it once no current worker is live plus one 30-second gate snapshot, before starting the previous ones. The previous build's scheduler also cannot load the recurring job types this build added. It retries and then disables those entries, and only a current worker's boot re-enabled them, so after a rolling deploy the departed-member token sweep and three others could stay off. A keeper on the worker role now re-runs the boot registration every five minutes. AddOrUpdate reschedules an entry that carries a scheduling error and leaves a healthy one unchanged. Old builds cannot be made to ignore an unknown job type, so healing is the remedy.
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.
Summary
cap-sealed-payload-v1among its queues. Nothing is ever enqueued to it, and a previous build never lists it.flow.subworkflowchild's payload, and a replay or rerun fork of a sealed run.hangfire.serverat most once per 30 seconds. An unreadable table answers no.WorkflowSensitivePayloadSealer.SealCopy,WorkflowService.csStageForkedRunAsync).Reseal, which skips the gate. Unsealing that row made a later re-attach fail the handle's redactor fingerprint and lose the run's tail. No previous-version worker launches a Running row.POST api/admin/sealed-payloads/unseal(global admin,UnsealNonTerminalPayloadsCommand):Deploy and rollback
RollingUpdateincluded: no action needed.hangfire.server, or after 2 minutes without a heartbeat.hangfire.server, or 2 minutes without a heartbeat), then 30 s more.POST api/admin/sealed-payloads/unseal. Send it again ifraced > 0.NextExecution. The keeper restores them within 5 minutes of a current worker being live.Known limits
task_jsonbandnormalized_payload_json, and the receiving run does not mask the inherited value. Both are exactly the pre-0247 behaviour.[REDACTED].Test plan
Seal,ResealandSealCopytheories;AgentTaskSecrets.Resealtheory.ReplayandRerunFromNodeagainst three fleets (all current, previous-version worker live, no worker live).[REDACTED].Seal, or removing the no-live-worker rule, makes the matching tests fail.