From 152890f321075beede075314d37ff6451604c359 Mon Sep 17 00:00:00 2001 From: tv Date: Mon, 9 Nov 2026 20:29:32 +0000 Subject: document file: a clean stop marked, and `--keep-actor` writes on as the same actor A node writes as a fresh Automerge actor at each start, so no change number of an actor comes twice: one sent before a crash but not yet appended to the document file would otherwise be made again, differently. An epoch thus gathers a writer per start, and every transfer of its history carries a signature per writer. The document file now says when it holds everything the node wrote: * at a clean stop, after the last append, an empty `Closed` record, only where the file is up to the document's heads (ready, not rejoining); * as a node starts on the file, before anything is written, an empty `Opened` record, so a crash from then on leaves a file without the mark; * the header names the actor the node writes with (`current`). With `--keep-actor` (off by default; needs `--document-file`), a node starting on a file that ends with `Closed` writes on as that actor. A crash, a drop, a switch or a rejoin still starts a fresh one, so an epoch's writers come to one per node plus one per such event. lifecycle.md says so. --- src/swarm/document_file.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'src/swarm/document_file.rs') diff --git a/src/swarm/document_file.rs b/src/swarm/document_file.rs index e066e27..4c7843d 100644 --- a/src/swarm/document_file.rs +++ b/src/swarm/document_file.rs @@ -25,6 +25,20 @@ 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`). +pub(crate) async fn close(state: &AppState) { + let Some(file) = state.document_file.clone() else { + return; + }; + if state.remake.is_rejoining() || !state.is_ready().await { + return; + } + let heads = state.doc.read().await.doc.get_heads(); + file.close(&heads); +} + /// Writes what is due now (`DocumentFile::due`). Also called at shutdown. pub(crate) async fn flush(state: &AppState) { let Some(file) = state.document_file.clone() else { -- cgit v1.3.1