automerge / automerge-classic

A JSON-like data structure (a CRDT) that can be modified concurrently by different users, and merged again automatically.
http://automerge.org/
MIT License
14.75k stars 467 forks source link

Automerge.merge() is inconsistent #498

Closed yenrr closed 2 years ago

yenrr commented 2 years ago

When I tried to use Automerge.merge(doc1, doc2) sometimes merge works perfectly but for the 5th time that the documents are merged it does not merge correctly and the new changes are lost, it is like a caching or something like that. Any idea? thanks. Example: // Merge changeset with current history let result = automerge.init(); result = automerge.merge(currentInit, changes);

eduardo-vareto commented 2 years ago

I'm having a similar issue:

let bin = Automerge.save(Automerge.init())
for (let i = 0; i < 10; i++) {
  let docA = Automerge.load<any>(bin)
  let docB = Automerge.load<any>(bin)
  docA = Automerge.change(docA, (doc) => {
    doc.x = 42
  })
  docB = Automerge.change(docB, (doc) => {
    doc.x = 0
  })
  let docC = Automerge.merge(docA, docB)
  console.log(JSON.stringify(docC, null, 2)) // On each loop pass it may print a different value
}

Since I'm creating new documents from the same binary, shouldn't they share the same seed?

eduardo-vareto commented 2 years ago

Ok I found the issue, at least in my case. The problem here is the ActorID, which is kind the seed for the random algorithm doing the merge.

In my example, I only had to change to something like this to fix the issue:

let bin = Automerge.save(Automerge.init())
for (let i = 0; i < 10; i++) {
  let docA = Automerge.load<any>(bin, '40cc2977d920405282d30b7a43a297f5')
  let docB = Automerge.load<any>(bin, 'bd0f0221f61b4420a1ee1f226cb8ae4b')
  docA = Automerge.change(docA, (doc) => {
    doc.x = 42
  })
  docB = Automerge.change(docB, (doc) => {
    doc.x = 0
  })
  let docC = Automerge.merge(docA, docB)
  console.log(JSON.stringify(docC, null, 2))
}

@yenrr Maybe that is the cause of your issue as well?

PS: This is an unrealistic solution for an unrealistic problem. In a real application that wouldn't be a problem, i.e., we wouldn't have to manually specify the ActorIDs.

yenrr commented 2 years ago

Thanks @eduardo-vareto I will try, I think my issue is similar to yours, thanks for your answer.

ept commented 2 years ago

That's right. Every time you do Automerge.init() or Automerge.load(), that object gets a new random actorId, which is then used to resolve conflicts. You can hard-code the actorIds, but then you are responsible for ensuring that the same actorId is not used in two different places at the same time.

yenrr commented 2 years ago

Thank you so much @eduardo-vareto @ept for your answers, I was able to fix my problem and you were right my problems were related to actorId.