1
votes

I'm new to pouch/couch and looking for some guidance on handling conflicts. Specifically, I have an extension running pouchdb (distributed to two users). Then the idea is to have a pouchdb-server or couchdb (does it matter for this small a use case?) instance running remotely. The crux of my concern is handling conflicts, the data will be changing frequently and though the extensions won't be doing live sync, they will be syncing very often. I have conflict handling written into the data submission functions, however there could still be conflicts when syncing occurs with multiple users.

I was looking at the pouch-resolve-conflicts plugin and see immediately the author state:

"Conflict resolution should better be done server side to avoid hard to debug loops when multiple clients resolves conflicts on the same documents".

This makes sense to me, but I am unsure how to implement such conflict resolution. The only way I can think would be to place REST API layer in front of the remote database that handles all updates/conflicts etc with custom logic. But then how could I use the pouch sync functionality? At that point I may as well just use a different database.

I've just been unable to find any resources discussing how to implement conflict resolution server-side, in fact the opposite.

2

2 Answers

3
votes

With your use case, you could probably write to a local pouchdb instance and sync it with the master database. Then, you could have a daemon that automatically resolve conflicts on your master database.

Below is my approach to solve a similar problem.


I have made a NodeJS daemon that automatically resolve conflicts. It integrates deconflict, a NodeJS library that allows you to resolve a document in three ways:

  • Merge all revisions together
  • Keep the latest revisions (based on a custom key. Eg: updated_at)
  • Pick a certain revision (Here you can use your own logic)

Revision deconflict

The way I use CouchDB, every write is partial. We always take some changes and apply them to the latest document. With this approach, we can easily take the merge all revision strategy.

Conflict scanner

When the daemon boot, two processes are executed. One that go through all the changes. If a conflict is detected, it's added to a conflict queue.

Another process is executed and remain active: Continuous changes scanner. It listen to all new changes and add conflicted documents to the conflict queue

Queue processing

Another process is started and keeps polling the queue for new conflicted documents. It gets conflicted documents in batch and resolve them on by one. If there's not documents, it just wait a certain period and starts the polling again.

1
votes

Having worked a little bit with Redux I realized that the same concept of unidirectional flow would help me avoid the problem of conflicts altogether.

Redux flows like this...

The Redux Flow

So, my clientside code never write definitive data to the master database, instead they write insert/update/delete requests locally which PouchDB then pushes to the CouchDB master database. On the same server as the master CouchDB I have PouchDB in NodeJS replicating these requests. "Superviser" software in NodeJS examines each new request, changes their status to "processing" writes the requested updates, inserts and deletes, then marks the request "processed". To ensure they're processed one at time the code that receives each request, stuffs them into a FIFO. The processing code pulls them from the other end.

I'm not dealing with super high volume, so the latency is not a concern.

I'm also not facing a situation where numerous people might be trying to update exactly the same record at the same time. If that's your situation, your client-side update requests will need to specify the rev number and your "supervisors" will need to reject change requests that refer to a superseded version. You'll have to figure out how your client code would get and respond to those rejections.