Introduction to managing conflicts in NunDB
Conflict resolution is now, is a first-class citizen of NunDB, and in this post, I will show how to explore the new capabilities and features.
In this post, we will show how to deal with conflict resolution and take the max you can from the conflict resolution.
Create the DB
nun-db --user $NUN_USER -p $NUN_PWD --host "http://http.nundb.org" exec "create-db trelo-real-time-arbiter trelo-real-time-pwd arbiter;use-db trelo-real-time-arbiter trelo-real-time-pwd;keys;snapshot"
The important part of this command is create-db $user $pwd arbiter
the arbiter is the vital part o this command. We also have two other strategies, the none or newer.
In this post, the goal is to explore deeper the option of arbiter, but we will fly to the others.
Consensus Strategy None
Sample create-db $user $pwd none
or create-db $user $pwd
. In this case there will not be any try to resolve conflicts. Any old version trying to be set, set will be rejected with a version error.
const key = 'obj'; const version = 1; const save2 = await db1.setValueSafe(key, { name: 'name 2' }, version);// saved const save1 = await db2.setValueSafe(key, { name: 'name 1' }, version);// Fails because verion did not pump const value = await db1.getValueSafe(key);// Value { name: 'name 2'}
Consensus Strategy Newer
Sample create-db $user $pwd newer
in this case, there is one out-of-the-box conflict resolution strategy. In this case, the latest set is always the valid.
const key = 'obj'; const version = 1; const save2 = await db1.setValueSafe(key, { name: 'name 2' }, version);// saved const save1 = await db2.setValueSafe(key, { name: 'name 1' }, version);// saved const value = await db1.getValueSafe(key);// Value { name: 'name 1'}
Consensus Strategy Arbiter
Sample create-db $user $pwd arbiter.
This is a less simple one; in this case, the conflict will be resolved by an actor we call arbiter,
const key = 'obj'; const version = 1; const save2 = await db1.setValueSafe(key, { name: 'name 2' }, version);// saved const save1 = await db2.setValueSafe(key, { name: 'name 1' }, version);// works and triggers arbiter // What to do? const value = await db1.getValueSafe(key);// Value { name: 'name 1'}
How to create the Arbiter
The Arbiter is a database level, which means you need to connect as an arbiter for each db.
1. Connect as usual
const db = new NunDb(server_url, "test", "test-pwd");
Tell the server that you are an arbiter
await db.becameArbiter(resolverCallBack);
Set the conflict resolution
In this example, I am implementing a very simple auto-conflict resolution. Here I am concatenating the name with the conflicted name. Of course, all applications will need different implementations. Here the idea is if two users change the name of the same person, we concatenate both and save that as the name.
The callback has to return a promise, so depending on the application, you can implement long tasks, like letting the user choose in the UI. While the key is in a resolution conflict state, it will stay with version -2,
and any new change to the same key will be added to a stack
of conflicts to be resolved one after the other.
const resolverCallBack = (e) => { // Resolve the conflict by concatenating the names const concat = e.values.map( _ => _.value.name).join(', '); console.log('Will finally resolve to',concat); return Promise.resolve({ id: e.values.at(0).id, value: { name: concat } }); }; await db.becameArbiter(resolverCallBack);// Repeated from the last code
How will the code work in the end?
const key = 'obj'; const version = 1; const save2 = await db1.setValueSafe(key, { name: 'name 2' }, version);// saved const save1 = await db2.setValueSafe(key, { name: 'name 1' }, version);// works and triggers arbiter // What to do? const value = await db1.getValueSafe(key);// Value { name: 'name 1'}
All together
const db = new NunDb("ws://nun-db-1.localhost:3058", "trelo-real-time-arbiter", "trelo-real-time-pwd"); const resolverCallBack = (e) => { const concat = e.values.map( _ => _.value.name).join(', '); console.log('Will finally resolve to',concat); return Promise.resolve({ id: e.values.at(0).id, value: { name: concat } }); }; await db.becameArbiter(resolverCallBack);// Repeated from the last code const version = parseInt(new Date().getTime() / 1000, 10);// Changed to make sure the version is unic on each execution const key = 'obj_new'; const save2 = await db.setValueSafe(key, { name: 'name 2' }, version);// saved const save1 = await db.setValueSafe(key, { name: 'name 1' }, version);// works and triggers arbiter // A few secounds latter const value = await db.getValueSafe(key);// Value { name: 'name 1, name 2, name 1'}
Conclusion
In this post, we quickly demonstrated how to use the new NunDB capabilities to resolve conflicts. Most applications may not need many details to resolve conflicts, and therefore the last write wing, Newer
should cover most of the user cases. In the cases where users want fine-granted control and manipulate conflicts in a much deeper way, the Arbiter
will be the tool for that.
In our next post, we will show an example of the implementation of that using also offline first new NunDB js capabilities, which will show the power of conflict resolution in more complex conditions, dealing with UI react components and others.
- Code Coverage for Rust Projects with GitHub Actions
- NunDb is now referenced in the Database of Databases
- Real-time Medical Image Collaboration POC Made Easy with OHIF and Nun-db
- How to create users with different permission levels in Nun-db
- Match vs Hashmap! Which one is faster in rust?
- Towards a More Secure Nun-db: Our Latest Security Enhancements
- Building a Trello-like React/Redux App with NunDB with offline and conflict resolution features
- Introduction to managing conflicts in NunDB
- Keepin up with Nun-db 2023
- The new storage engine of Nun-db
- Stop procrastinating and just fix your flaky tests, it may be catching nasty bugs
- An approach to hunt and fix non-reproducible bugs - Case study - Fixing a race conditions in Nun-db replication algorithm in rust
- NunDB the debug command
- Keeping up with Nun-db 2021
- Writing a prometheus exporter in rust from idea to grafana chart
- Integration tests in rust a multi-process test example
- Leader election in rust the journey towards implementing nun-db leader election
- How to make redux TodoMVC example a real-time multiuser app with nun-db in 10 steps
- A fast-to-sync/search and space-optimized replication algorithm written in rust, The Nun-db data replication model
- NunDb How to backup one or all databases
- How to create your simple version of google analytics real-time using Nun-db
- Migrating a chat bot feature from Firebase to Nun-db
- Keepin' up with NunDB
- Going live with NunDB