diff options
| author | tv <tv@krebsco.de> | 2026-11-09 22:27:40 +0000 |
|---|---|---|
| committer | tv <tv@krebsco.de> | 2026-11-11 23:29:13 +0100 |
| commit | 5b2232b14aba5d72f07b9d1ae889f9951d6d2569 (patch) | |
| tree | 15dd8d74679e012bdfbd16a3c13c47ee33b694af /src | |
| parent | 2a4316f4a256012120337064df150944880b6453 (diff) | |
document file: write as a fresh actor once the file is marked closed
With --keep-actor, a node on a file whose last record is Closed writes on
as the actor the header names, which takes the file holding every change
of that actor. At a clean stop, the node's own workers go on writing
until it exits: its alive stamp in the member list, votes, a
coordinator's commit. One committed after Closed was appended, and sent
by the still-running sync, would be a change of that actor the file
lacks, and the next start would make another change with its number.
swarm::document_file::close now takes the document's write lock, marks
the file at the heads it sees there, and sets a fresh actor before
letting go: what the node writes after is no change of the marked actor.
A record the file worker appends after Closed (such a write, flushed)
leaves the file unmarked, which starts a fresh actor next time.
Diffstat (limited to 'src')
| -rw-r--r-- | src/swarm/document_file.rs | 17 |
1 files changed, 15 insertions, 2 deletions
diff --git a/src/swarm/document_file.rs b/src/swarm/document_file.rs index 4c7843d..c833656 100644 --- a/src/swarm/document_file.rs +++ b/src/swarm/document_file.rs @@ -28,6 +28,7 @@ pub(crate) async fn run(state: AppState) { /// At a clean stop, after the last `flush`: the file marked as holding everything this /// node wrote (`DocumentFile::close`), where it does (ready, not rejoining, up to the /// document's heads), so a restart may write on as the same actor (`--keep-actor`). +/// The node writes as a fresh actor from then on. pub(crate) async fn close(state: &AppState) { let Some(file) = state.document_file.clone() else { return; @@ -35,8 +36,20 @@ pub(crate) async fn close(state: &AppState) { if state.remake.is_rejoining() || !state.is_ready().await { return; } - let heads = state.doc.read().await.doc.get_heads(); - file.close(&heads); + // Under the write lock, and leaving the document with a fresh actor: whatever the + // node still writes until it exits (its alive stamp, a vote, a coordinator's commit: + // nothing stops them) is no change of the actor the file marks as complete. + let doc = Arc::clone(&state.doc); + let closed = tokio::task::spawn_blocking(move || { + let mut replica = doc.blocking_write(); + let heads = replica.doc.get_heads(); + file.close(&heads); + replica.doc.set_actor(crate::document::ActorId::random()); + }) + .await; + if let Err(error) = closed { + tracing::error!(error = %error, "Marking the document file as closed failed"); + } } /// Writes what is due now (`DocumentFile::due`). Also called at shutdown. |
