The boot lift yields to a Deploy by the continuity's own yield test
The lift kept an abort running for any awaits_handover Deploy, so a braked Deploy released a held record to an abort that stopped the only copy of the body. One predicate, deploy_the_continuity_yields_to (candidate unproven, a waiting Deploy that deploy_can_move), is now what both the deadline's yield and the lift ask (rulings c2d-yield-only-unproven-to-movable, c2c-lift-defers-on-observe-error). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
b993615c69
commit
c4edc66c48
1 changed files with 37 additions and 9 deletions
|
|
@ -2104,7 +2104,8 @@ impl DeploymentTransaction {
|
|||
|
||||
/// A live deployment whose next move is the handover: not ended, not
|
||||
/// aborting, one phase short of `takes_target_at`. Derived, never stored.
|
||||
/// Read by the continuity's yield and the boot lift.
|
||||
/// Read by `deploy_the_continuity_yields_to`: the continuity's yield and the
|
||||
/// boot lift.
|
||||
fn awaits_handover(&self) -> bool {
|
||||
self.command_kind == CommandKind::Deploy
|
||||
&& self.completion.is_none()
|
||||
|
|
@ -5810,12 +5811,20 @@ impl Engine {
|
|||
|| transaction.completion.is_some()
|
||||
|| abort.candidate_cleanup != CleanupEvidence::Pending
|
||||
|| abort.lease_withdrawal == CleanupEvidence::Complete
|
||||
|| snapshot
|
||||
.on_target(&transaction.target)
|
||||
.any(|other| other.value.awaits_handover())
|
||||
{
|
||||
return LiftDecision::KeepAborting;
|
||||
}
|
||||
match now_millis() {
|
||||
Ok(now) => {
|
||||
if self
|
||||
.deploy_the_continuity_yields_to(snapshot, transaction, now)
|
||||
.is_some()
|
||||
{
|
||||
return LiftDecision::KeepAborting;
|
||||
}
|
||||
}
|
||||
Err(error) => return LiftDecision::Undecided(error),
|
||||
}
|
||||
let lifted = (|| -> Result<bool> {
|
||||
let plan = required(&transaction.plan, "transaction plan")?;
|
||||
let expected = required(&transaction.expected, "Expected projection")?;
|
||||
|
|
@ -8568,6 +8577,25 @@ impl Engine {
|
|||
|| matches!(self.deployment_authorization(deploy, now), Ok(Some(_)))
|
||||
}
|
||||
|
||||
/// The deployment a continuity gives way to: one on its target that awaits
|
||||
/// the handover and can take it, while the continuity's own candidate is
|
||||
/// unproven (ruling c2d-yield-only-unproven-to-movable). The one test the
|
||||
/// deadline's yield and the boot lift's abort both ask, so neither stops a
|
||||
/// serving candidate, or one a braked Deploy cannot replace.
|
||||
fn deploy_the_continuity_yields_to<'a>(
|
||||
&self,
|
||||
snapshot: &'a ControlSnapshot,
|
||||
continuity: &DeploymentTransaction,
|
||||
now: u64,
|
||||
) -> Option<&'a Stored<DeploymentTransaction>> {
|
||||
if continuity.serving_proven() {
|
||||
return None;
|
||||
}
|
||||
snapshot.on_target(&continuity.target).find(|other| {
|
||||
other.value.awaits_handover() && self.deploy_can_move(other, now)
|
||||
})
|
||||
}
|
||||
|
||||
fn lifecycle_allows(&self, current: &Stored<DeploymentTransaction>, now: u64) -> Result<bool> {
|
||||
let plan = required(¤t.value.plan, "continuity plan")?;
|
||||
let binding = plan.parsed_inputs()?.1;
|
||||
|
|
@ -9763,11 +9791,11 @@ impl Engine {
|
|||
PhaseEnd::RetryForward => {
|
||||
let snapshot = ControlSnapshot::read(&self.options.state_store)?;
|
||||
let now = now_millis()?;
|
||||
if let Some(deploy) = snapshot.on_target(&transaction.target).find(|other| {
|
||||
transaction.yields_to_a_deploy()
|
||||
&& other.value.awaits_handover()
|
||||
&& self.deploy_can_move(other, now)
|
||||
}) {
|
||||
if let Some(deploy) = transaction
|
||||
.yields_to_a_deploy()
|
||||
.then(|| self.deploy_the_continuity_yields_to(&snapshot, transaction, now))
|
||||
.flatten()
|
||||
{
|
||||
self.begin_post_fencing_abort(
|
||||
current,
|
||||
anyhow!(
|
||||
|
|
|
|||
Loading…
Reference in a new issue