Skip to content

Commit dae61d5

Browse files
afckclaude
andcommitted
Poison chain worker on cancelled save
If the caller's future is dropped between `write_batch` landing in storage and `post_save` refreshing the in-memory register views, the views' cached `stored_value` goes stale while storage has advanced. Setting the poison flag up front and clearing it only after `save` fully returns makes the poison sticky on both error and cancellation, so the next access evicts and reloads from storage. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent ef3c611 commit dae61d5

1 file changed

Lines changed: 11 additions & 29 deletions

File tree

linera-core/src/chain_worker/state.rs

Lines changed: 11 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2136,39 +2136,21 @@ where
21362136

21372137
/// Stores the chain state in persistent storage.
21382138
///
2139-
/// If the save fails, the worker is marked as poisoned and must be reloaded.
2139+
/// Poisons the worker up front and clears the flag only after the underlying
2140+
/// `save()` has fully returned. This makes the poison sticky on both error
2141+
/// and cancellation: if the caller's future is dropped between the storage
2142+
/// write landing and `post_save()` refreshing the in-memory views, the flag
2143+
/// stays set and the next access will evict and reload from storage.
21402144
#[instrument(skip_all, fields(
21412145
chain_id = %self.chain_id()
21422146
))]
21432147
async fn save(&mut self) -> Result<(), WorkerError> {
2144-
let chain_id = self.chain_id();
2145-
let tip_height = self.chain.tip_state.get().next_block_height;
2146-
let used_blobs_count = self
2147-
.chain
2148-
.execution_state
2149-
.system
2150-
.used_blobs
2151-
.indices()
2152-
.await
2153-
.map(|v| v.len())
2154-
.unwrap_or(usize::MAX);
2155-
warn!(
2156-
%chain_id, %tip_height, used_blobs_count,
2157-
"used_blobs_trace: save: START"
2158-
);
2159-
if let Err(error) = self.chain.save().await {
2160-
warn!(
2161-
%chain_id, %tip_height, %error,
2162-
"used_blobs_trace: save: FAILED"
2163-
);
2164-
tracing::error!(?error, "Chain save failed; marking worker as poisoned");
2165-
self.poisoned = true;
2166-
return Err(WorkerError::PoisonedWorker);
2167-
}
2168-
warn!(
2169-
%chain_id, %tip_height,
2170-
"used_blobs_trace: save: OK"
2171-
);
2148+
self.poisoned = true;
2149+
self.chain.save().await.map_err(|error| {
2150+
tracing::error!(?error, "Chain save failed; worker poisoned");
2151+
WorkerError::PoisonedWorker
2152+
})?;
2153+
self.poisoned = false;
21722154
Ok(())
21732155
}
21742156
}

0 commit comments

Comments
 (0)