automerge / automerge-swift-archived

Wrapper around Swift types that can be modified concurrently by different users, and merged again automatically (a CRDT).
MIT License
159 stars 14 forks source link

Adding test case that fails intermittently #29

Closed adamwulf closed 3 years ago

adamwulf commented 3 years ago

This is a barebones example that will fail to merge two documents ~ 50% of the time. The model being synced is a Board that contains a Card that has a Tint.

lightsprint09 commented 3 years ago

Hey Adam, thanks for bringing this up.

I will look into it soon.

lightsprint09 commented 3 years ago

My initial thought is that is should fail 100% of the time.

I guess this is related to a similar issue on the main repo: https://github.com/automerge/automerge/issues/273

adamwulf commented 3 years ago

Yeah, the fact that it's intermittent it what's strangest to me. even with in a single test run, some of the documents merge fine, others don't.

Interestingly, when the test fails, the console output includes:

2021-04-29 11:51:19.661709-0500 xctest[11219:386447] Failed to find immediate ancestor for symbol at 0x4 - 0xa

when the test succeeds, that message doesn't show in the console. I couldn't find any references to that log message in automerge source, and google turns up nothing, I'm not sure what's printing that.

adamwulf commented 3 years ago

I've just pushed up some additional commits that have explicit [UInt8] document data and [[UInt8]] changes data that either always pass or always fail. I have 5 document/change pairs of both passing and failing.

Interestingly, while every doc[x], change[x] of the successful pairs passes, not every doc[x], change[y] of the successful pairs passes. This suggests to me that both the data generated for the initial document and the data for the changes is somehow incorrect.

(though, i could be misunderstanding how changes are applied to documents. if it relies on a clock timestamp, then a valid change from a past doc/change pair could not be applied to a document generated now.)

endeavour42 commented 3 years ago

can this be due to timestamp difference? (float seconds vs int milliseconds)? @lightsprint09

lightsprint09 commented 3 years ago

I don't really know what happens here. I don't know very much about the Automerge internals. Maybe @orionz can help to clarify what happens here.

orionz commented 3 years ago

Hey - just saw this thread - I'll start looking into this

orionz commented 3 years ago

Ok - I had a chance to review this...

In automerge if you make a base document twice and merge it all the fields and objects will conflict on the initial merge. What's happening here is that each Document is re-creating the base doc

    var firstModel = Document(AMBoard(id: id, cards: []))
    // ...
    var applyModel1 = Document(AMBoard(id: id, cards: []))

Each of these documents has a random actorID and that actorID is used to determine the default winner of the conflict - which is why you're seeing it pass or fail 50% of the time. If you hard-coded the actorID's you could make it pass or fail 100% of the time ... however to get the behavior you're looking for each document should only have one creator. We can simulate this like this...

    var firstModel = Document(AMBoard(id: id, cards: []))
    let savedData = firstModel.save()
    // ...
    var applyModel2: Document<AMBoard> = Document(data: savedData)
    // ...
    var mergeModel1: Document<AMBoard> = Document(data: savedData)
    // ...

In a normal app each board has one creator and the peer would need to sync with it getting its changes remotely before reading or writing to it. The sync API was just finished and isn't yet exposed over the C api (I'm on that now). But this is a decent approximation of it.

lightsprint09 commented 3 years ago

Thanks @orionz I close this for now