Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatic conflict handling #1414

Open
paultranvan opened this issue Oct 31, 2023 · 0 comments
Open

Automatic conflict handling #1414

paultranvan opened this issue Oct 31, 2023 · 0 comments

Comments

@paultranvan
Copy link
Contributor

paultranvan commented Oct 31, 2023

It happens that a document update results in a 409 conflict, because of some parallel treatments on the same document
Typically, this can happen when an app creates a doc, and wants to update it later, while a service, triggered by the document creation, updates the very same document.

This is troublesome as it can happen on any update. Moreover, some of those conflicts could be automatically handled, typically, when the update occurs on different fields.

For instance:

const doc = createDoc({

// doc creation by app A
{
  _id: 1,
  _rev: 1,
  name: 'foo'
}

// doc udpate by service B
{
  _id: 1,
  _rev: 2,
  name: 'foo',
  count: 1
}

// doc udpate by app A
{
  _id: 1,
  _rev: 1,
  name: 'bar'
}
=> 409 conflict

// doc update after automatic conflict resolution
{
  _id: 1,
  _rev: 3,
  name: 'bar',
  count: 1
}

Note however, that realtime can be used to solve this, as the doc should be automatically updated to the latest version. So this mechanism is only useful for use-cases without realtime (or when realtime is not properly used)

To achieve this automatic conflict resolution, a three-way merge could be done, by comparing:

  • The doc to update
  • The current doc version
  • The common ancestor

To get those docs, several solutions could be envisioned:

Solution 1

A new API allows the client to provide the old doc along with the new doc to update, like this:

client.safeSave({oldDoc, newDoc})

If a conflict occurs, the current doc is queried from the database.

  • Advantage: it's reliable
  • Disadvantage: a new API to use, and more work for the clients

Solution 2

The client uses the same save API. When a conflict arrives, we query both the current doc, and the common ancestor thanks to the old revision and CouchDB history.

  • Advantage: No change for the client
  • Disadvantage: the CouchDB history is not reliable anymore, because of the very aggressive compaction scheduling, that happens after database writes. When the compaction is run, all the docs body from the old revisions are removed. And we noticed that the compaction can happen 1 minute after an update.

Solution 3

The client uses the same save API. When a conflict arrives, we check the doc version in the redux store, which is actually the common ancestor. If the revision differs, we query only the current doc version from the database.

  • Advantage: No change for the client
  • Disadvantage: Only works when the store is properly used

Strategy

Solution 2 does not seem reliable enough. Solution 3 sounds great, but only when the store is used. And Solution 1 works in any case, but it forces clients to use a new API.
=> We can exclude Solution 2, and implement both Solution 1 & 3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant