automerge / autosurgeon

MIT License
275 stars 17 forks source link

Can't use reconcile/hydrate with an Automerge Transaction due to StaleHeads #43

Open aran opened 11 months ago

aran commented 11 months ago

Here is some working code with AutoCommit

pub fn log_draft(editor: RustOpaque<Mutex<LocalEditor>>) -> Result<()> {
    let mut editor = editor.lock().unwrap();
    let LocalEditor { doc, .. } = &mut *editor;
    let mut demo: DemoSchema = hydrate(doc).unwrap();
    demo.draft
        .splice(0, demo.draft.as_str().chars().count() as isize, "");
    demo.edit_counter.increment(1);
    reconcile(doc, demo)?;
    Ok(())
}

If I change the type of the doc from AutoCommit to Automerge, and use a transaction:


    let mut editor = editor.lock().unwrap();
    let LocalEditor { doc, .. } = &mut *editor;
    let mut demo: DemoSchema = hydrate(doc).unwrap();
    demo.draft
        .splice(0, demo.draft.as_str().chars().count() as isize, "");
    demo.edit_counter.increment(1);
    let mut tx = doc.transaction();
    reconcile(&mut tx, demo)?;
    let _ = tx.commit();
    Ok(())
}```

I am seeing StaleHeads exception on a certain synchronization flow. 

I wish I had a clean repro, but I don't. I was seeing the error with the Automerge code, but not with the AutoCommit code, in the following scenario on a wasm build. I'm happy to work with you over Slack on chasing it down if you're interested.

Client 1: Make edit A, sync protocol until quiescent. (heads are now, say, ["1234"])
Client 2: Sync protocol, edit B, sync protocol (heads are now ["5678"])
Client 1: Edit C -> fails on reconcile with StaleHeads. Expected heads are ["9abc"], actual heads are ["9abc", "5678"]
alexjg commented 8 months ago

This is most likely caused because at the moment autosurgeon assumes that you will use it in a loop where you hydrate, make modifications to the data, and then reconcile, without changing the document in between. This is important for splice because it accumulates updates based on the state of the document at the time you called hydrate.

However, we do have the facilities in automerge to perform updates as at some point in time now (the API is a bit awkward but it can be done), so the solution is to save the heads in the Text at the time that we call hydrate and then when we call reconcile apply the changes as at the given heads.