-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
Copy pathtransactions-conflicts-revisions.html
69 lines (67 loc) · 36.9 KB
/
transactions-conflicts-revisions.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
<!doctype html>
<html lang="en" dir="ltr" class="docs-wrapper plugin-docs plugin-id-default docs-version-current docs-doc-page docs-doc-id-transactions-conflicts-revisions" data-has-hydrated="false">
<head>
<meta charset="UTF-8">
<meta name="generator" content="Docusaurus v3.7.0">
<title data-rh="true">Transactions, Conflicts and Revisions | RxDB - JavaScript Database</title><meta data-rh="true" name="viewport" content="width=device-width,initial-scale=1"><meta data-rh="true" name="twitter:card" content="summary_large_image"><meta data-rh="true" property="og:image" content="https://rxdb.info/img/rxdb_social_card.png"><meta data-rh="true" name="twitter:image" content="https://rxdb.info/img/rxdb_social_card.png"><meta data-rh="true" property="og:url" content="https://rxdb.info/transactions-conflicts-revisions.html"><meta data-rh="true" property="og:locale" content="en"><meta data-rh="true" name="docusaurus_locale" content="en"><meta data-rh="true" name="docsearch:language" content="en"><meta data-rh="true" name="docusaurus_version" content="current"><meta data-rh="true" name="docusaurus_tag" content="docs-default-current"><meta data-rh="true" name="docsearch:version" content="current"><meta data-rh="true" name="docsearch:docusaurus_tag" content="docs-default-current"><meta data-rh="true" property="og:title" content="Transactions, Conflicts and Revisions | RxDB - JavaScript Database"><meta data-rh="true" name="description" content="Learn RxDB's approach to local and replication conflicts. Discover how incremental writes and custom handlers keep your app stable and efficient."><meta data-rh="true" property="og:description" content="Learn RxDB's approach to local and replication conflicts. Discover how incremental writes and custom handlers keep your app stable and efficient."><link data-rh="true" rel="icon" href="/img/favicon.ico"><link data-rh="true" rel="canonical" href="https://rxdb.info/transactions-conflicts-revisions.html"><link data-rh="true" rel="alternate" href="https://rxdb.info/transactions-conflicts-revisions.html" hreflang="en"><link data-rh="true" rel="alternate" href="https://rxdb.info/transactions-conflicts-revisions.html" hreflang="x-default"><link rel="preconnect" href="https://www.google-analytics.com">
<link rel="preconnect" href="https://www.googletagmanager.com">
<script async src="https://www.googletagmanager.com/gtag/js?id=G-62D63SY3S0"></script>
<script>function gtag(){dataLayer.push(arguments)}window.dataLayer=window.dataLayer||[],gtag("js",new Date),gtag("config","G-62D63SY3S0",{})</script>
<link rel="preconnect" href="https://www.googletagmanager.com">
<script>window.dataLayer=window.dataLayer||[]</script>
<script>!function(e,t,a,n){e[n]=e[n]||[],e[n].push({"gtm.start":(new Date).getTime(),event:"gtm.js"});var g=t.getElementsByTagName(a)[0],m=t.createElement(a);m.async=!0,m.src="https://www.googletagmanager.com/gtm.js?id=GTM-PL63TR5",g.parentNode.insertBefore(m,g)}(window,document,"script","dataLayer")</script><link rel="stylesheet" href="/assets/css/styles.bc483400.css">
<script src="/assets/js/runtime~main.68ae4ffa.js" defer="defer"></script>
<script src="/assets/js/main.90e814da.js" defer="defer"></script>
</head>
<body class="navigation-with-keyboard">
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-PL63TR5" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<script>!function(){function t(t){document.documentElement.setAttribute("data-theme",t)}var e=function(){try{return new URLSearchParams(window.location.search).get("docusaurus-theme")}catch(t){}}()||function(){try{return window.localStorage.getItem("theme")}catch(t){}}();t(null!==e?e:"dark")}(),function(){try{const n=new URLSearchParams(window.location.search).entries();for(var[t,e]of n)if(t.startsWith("docusaurus-data-")){var a=t.replace("docusaurus-data-","data-");document.documentElement.setAttribute(a,e)}}catch(t){}}()</script><div id="__docusaurus"><div role="region" aria-label="Skip to main content"><a class="skipToContent_fXgn" href="#__docusaurus_skipToContent_fallback">Skip to main content</a></div><nav aria-label="Main" class="navbar navbar--fixed-top"><div class="navbar__inner"><div class="navbar__items"><button aria-label="Toggle navigation bar" aria-expanded="false" class="navbar__toggle clean-btn" type="button"><svg width="30" height="30" viewBox="0 0 30 30" aria-hidden="true"><path stroke="currentColor" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2" d="M4 7h22M4 15h22M4 23h22"></path></svg></button><a class="navbar__brand" href="/"><div class="navbar__logo"><img src="/files/logo/logo.svg" alt="RxDB Logo" class="themedComponent_mlkZ themedComponent--light_NVdE"><img src="/files/logo/logo.svg" alt="RxDB Logo" class="themedComponent_mlkZ themedComponent--dark_xIcU"></div><b class="navbar__title text--truncate">RxDB</b></a></div><div class="navbar__items navbar__items--right"><a class="navbar__item navbar__link" href="/consulting/">Support</a><a class="navbar__item navbar__link" href="/premium/">Premium</a><a class="navbar__item navbar__link" href="/overview.html">Docs</a><div class="navbarSearchContainer_Bca1"></div></div></div><div role="presentation" class="navbar-sidebar__backdrop"></div></nav><div style="position:fixed;display:block;z-index:10;height:1.5px;background-color:var(--color-top);top:62.5px;border-top-right-radius:2px;border-bottom-right-radius:2px;width:0%"></div><div id="__docusaurus_skipToContent_fallback" class="main-wrapper mainWrapper_z2l0"><div class="docsWrapper_hBAB"><button aria-label="Scroll back to top" class="clean-btn theme-back-to-top-button backToTopButton_sjWU" type="button"></button><div class="docRoot_UBD9"><aside class="theme-doc-sidebar-container docSidebarContainer_YfHR"><div class="sidebarViewport_aRkj"><div class="sidebar_njMd"><nav aria-label="Docs sidebar" class="menu thin-scrollbar menu_SIkG"><ul class="theme-doc-sidebar-menu menu__list"><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist menu__link--sublist-caret" role="button" aria-expanded="true" href="/overview.html">Getting Started with RxDB</a></div><ul style="display:block;overflow:visible;height:auto" class="menu__list"><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/overview.html">Overview</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/quickstart.html">🚀 Quickstart</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/install.html">Installation</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/dev-mode.html">Development Mode</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/tutorials/typescript.html">TypeScript Setup</a></li></ul></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist menu__link--sublist-caret" role="button" aria-expanded="true" href="/rx-database.html">Core Entities</a></div><ul style="display:block;overflow:visible;height:auto" class="menu__list"><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/rx-database.html">RxDatabase</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/rx-schema.html">RxSchema</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/rx-collection.html">RxCollection</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/rx-document.html">RxDocument</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/rx-query.html">RxQuery</a></li></ul></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item menu__list-item--collapsed"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist menu__link--sublist-caret" role="button" aria-expanded="false" href="/rx-storage.html">💾 Storages</a></div></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item menu__list-item--collapsed"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist menu__link--sublist-caret" role="button" aria-expanded="false" href="/schema-validation.html">Storage Wrappers</a></div></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item menu__list-item--collapsed"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist menu__link--sublist-caret" role="button" aria-expanded="false" href="/replication.html">🔄 Replication</a></div></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item menu__list-item--collapsed"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist menu__link--sublist-caret" role="button" aria-expanded="false" href="/rx-server.html">Server</a></div></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist menu__link--sublist-caret menu__link--active" role="button" aria-expanded="true" href="/transactions-conflicts-revisions.html">How RxDB works</a></div><ul style="display:block;overflow:visible;height:auto" class="menu__list"><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link menu__link--active" aria-current="page" tabindex="0" href="/transactions-conflicts-revisions.html">Transactions Conflicts Revisions</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/query-cache.html">Query Cache</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/plugins.html">Creating Plugins</a></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-2 menu__list-item"><a class="menu__link" tabindex="0" href="/errors.html">Errors</a></li></ul></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item menu__list-item--collapsed"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist menu__link--sublist-caret" role="button" aria-expanded="false" href="/migration-schema.html">Advanced Features</a></div></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item menu__list-item--collapsed"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist menu__link--sublist-caret" role="button" aria-expanded="false" href="/rx-storage-performance.html">Performance</a></div></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item menu__list-item--collapsed"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist menu__link--sublist-caret" role="button" aria-expanded="false" href="/releases/16.0.0.html">Releases</a></div></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item menu__list-item--collapsed"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist menu__link--sublist-caret" role="button" aria-expanded="false" href="/offline-first.html">Articles</a></div></li><li class="theme-doc-sidebar-item-link theme-doc-sidebar-item-link-level-1 menu__list-item"><a class="menu__link" href="/contribution.html">Contribute & Innovate with RxDB</a></li><li class="theme-doc-sidebar-item-category theme-doc-sidebar-item-category-level-1 menu__list-item menu__list-item--collapsed"><div class="menu__list-item-collapsible"><a class="menu__link menu__link--sublist menu__link--sublist-caret" role="button" aria-expanded="false" href="/consulting/">Contact</a></div></li></ul></nav></div></div></aside><main class="docMainContainer_TBSr"><div class="container padding-top--md padding-bottom--lg"><div class="row"><div class="col docItemCol_VOVn"><div class="docItemContainer_Djhp"><article><nav class="theme-doc-breadcrumbs breadcrumbsContainer_Z_bl" aria-label="Breadcrumbs"><ul class="breadcrumbs" itemscope="" itemtype="https://schema.org/BreadcrumbList"><li class="breadcrumbs__item"><a aria-label="Home page" class="breadcrumbs__link" href="/"><svg viewBox="0 0 24 24" class="breadcrumbHomeIcon_YNFT"><path d="M10 19v-5h4v5c0 .55.45 1 1 1h3c.55 0 1-.45 1-1v-7h1.7c.46 0 .68-.57.33-.87L12.67 3.6c-.38-.34-.96-.34-1.34 0l-8.36 7.53c-.34.3-.13.87.33.87H5v7c0 .55.45 1 1 1h3c.55 0 1-.45 1-1z" fill="currentColor"></path></svg></a></li><li class="breadcrumbs__item"><span class="breadcrumbs__link">How RxDB works</span><meta itemprop="position" content="1"></li><li itemscope="" itemprop="itemListElement" itemtype="https://schema.org/ListItem" class="breadcrumbs__item breadcrumbs__item--active"><span class="breadcrumbs__link" itemprop="name">Transactions Conflicts Revisions</span><meta itemprop="position" content="2"></li></ul></nav><div class="tocCollapsible_ETCw theme-doc-toc-mobile tocMobile_ITEo"><button type="button" class="clean-btn tocCollapsibleButton_TO0P">On this page</button></div><div class="theme-doc-markdown markdown"><header><h1>Transactions, Conflicts and Revisions</h1></header>
<p>In contrast to most SQL databases, RxDB does not have the concept of relational, ACID transactions. Instead, RxDB has to apply different techniques that better suit the offline-first, client side world where it is not possible to create a transaction between multiple maybe-offline client devices.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="why-rxdb-does-not-have-transactions">Why RxDB does not have transactions<a href="#why-rxdb-does-not-have-transactions" class="hash-link" aria-label="Direct link to Why RxDB does not have transactions" title="Direct link to Why RxDB does not have transactions"></a></h2>
<p>When talking about transactions, we mean <a href="https://en.wikipedia.org/wiki/ACID" target="_blank" rel="noopener noreferrer">ACID transactions</a> that guarantee the properties of atomicity, consistency, isolation and durability.
With an ACID transaction you can mutate data dependent on the current state of the database. It is ensured that no other database operations happen in between your transaction and after the transaction has finished, it is guaranteed that the new data is actually written to the disc.</p>
<p>To implement ACID transactions on a <strong>single server</strong>, the database has to keep track on who is running transactions and then schedule these transactions so that they can run in isolation.</p>
<p>As soon as you have to split your database on <strong>multiple servers</strong>, transaction handling becomes way more difficult. The servers have to communicate with each other to find a consensus about which transaction can run and which has to wait. Network connections might break, or one server might complete its part of the transaction and then be required to roll back its changes because of an error on another server.</p>
<p>But with RxDB you have <strong>multiple clients</strong> that can go randomly online or offline. The users can have different devices and the clock of these devices can go off by any time. To support ACID transactions here, RxDB would have to make the whole world stand still for all clients, while one client is doing a write operation. And even that can only work when all clients are online. Implementing that might be possible, but at the cost of an unpredictable amount of performance loss and not being able to support <a href="/offline-first.html">offline-first</a>.</p>
<blockquote>
<p>A single write operation to a document is the only atomic thing you can do in RxDB.</p>
</blockquote>
<p>The benefits of not having to support transactions:</p>
<ul>
<li>Clients can read and write data without blocking each other.</li>
<li>Clients can write data while being <strong>offline</strong> and then replicate with a server when they are <strong>online</strong> again, called <a href="/offline-first.html">offline-first</a>.</li>
<li>Creating a compatible backend for the replication is easy so that RxDB can replicate with any existing infrastructure.</li>
<li>Optimizations like <a href="/rx-storage-sharding.html">Sharding</a> can be used.</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="revisions">Revisions<a href="#revisions" class="hash-link" aria-label="Direct link to Revisions" title="Direct link to Revisions"></a></h2>
<p>Working without transactions leads to having undefined state when doing multiple database operations at the same time. Most client side databases rely on a last-write-wins strategy on write operations. This might be a viable solution for some cases, but often this leads to strange problems that are hard to debug.</p>
<p>Instead, to ensure that the behavior of RxDB is <strong>always predictable</strong>, RxDB relies on <strong>revisions</strong> for version control. Revisions work similar to <a href="https://martinfowler.com/articles/patterns-of-distributed-systems/lamport-clock.html" target="_blank" rel="noopener noreferrer">Lamport Clocks</a>.</p>
<p>Each document is stored together with its revision string, that looks like <code>1-9dcca3b8e1a</code> and consists of:</p>
<ul>
<li>The revision height, a number that starts with <code>1</code> and is increased with each write to that document.</li>
<li>The database instance token.</li>
</ul>
<p>An operation to the RxDB data layer does not only contain the new document data, but also the previous document data with its revision string. If the previous revision matches the revision that is currently stored in the database, the write operation can succeed. If the previous revision is <strong>different</strong> than the revision that is currently stored in the database, the operation will throw a <code>409 CONFLICT</code> error.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="conflicts">Conflicts<a href="#conflicts" class="hash-link" aria-label="Direct link to Conflicts" title="Direct link to Conflicts"></a></h2>
<p>There are two types of conflicts in RxDB, the <strong>local conflict</strong> and the <strong>replication conflict</strong>.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="local-conflicts">Local conflicts<a href="#local-conflicts" class="hash-link" aria-label="Direct link to Local conflicts" title="Direct link to Local conflicts"></a></h3>
<p>A local conflict can happen when a write operation assumes a different previous document state, then what is currently stored in the database. This can happen when multiple parts of your application do simultaneous writes to the same document. This can happen on a single browser tab, or when multiple tabs write at once or when a write appears while the document gets replicated from a remote server replication.</p>
<p>When a local conflict appears, RxDB will throw a <code>409 CONFLICT</code> error. The calling code must then handle the error properly, depending on the application logic.</p>
<p>Instead of handling local conflicts, in most cases it is easier to ensure that they cannot happen, by using <code>incremental</code> database operations like <a href="/rx-document.html">incrementalModify()</a>, <a href="/rx-document.html">incrementalPatch()</a> or <a href="/rx-collection.html">incrementalUpsert()</a>. These write operations have a build in way to handle conflicts by re-applying the mutation functions to the conflicting document state.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="replication-conflicts">Replication conflicts<a href="#replication-conflicts" class="hash-link" aria-label="Direct link to Replication conflicts" title="Direct link to Replication conflicts"></a></h2>
<p>A replication conflict appears when multiple clients write to the same documents at once and these documents are then replicated to the backend server.</p>
<p>When you replicate with the <a href="/replication-graphql.html">Graphql replication</a> and the <a href="/replication.html">replication primitives</a>, RxDB assumes that conflicts are <strong>detected</strong> and <strong>resolved</strong> at the client side.</p>
<p>When a document is send to the backend and the backend detected a conflict (by comparing revisions or other properties), the backend will respond with the actual document state so that the client can compare this with the local document state and create a new, resolved document state that is then pushed to the server again. You can read more about the replication protocol <a href="/replication.html#conflict-handling">here</a>.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="custom-conflict-handler">Custom conflict handler<a href="#custom-conflict-handler" class="hash-link" aria-label="Direct link to Custom conflict handler" title="Direct link to Custom conflict handler"></a></h2>
<p>A conflict handler is an object with two JavaScript functions:</p>
<ul>
<li>Detect if two document states are equal</li>
<li>Solve existing conflicts</li>
</ul>
<p>Because the conflict handler also is used for conflict detection, it will run many times on pull-, push- and write operations of RxDB. Most of the time it will detect that there is no conflict and then return.</p>
<p>Lets have a look at the <a href="https://github.com/pubkey/rxdb/blob/master/src/replication-protocol/default-conflict-handler.ts" target="_blank" rel="noopener noreferrer">default conflict handler</a> of RxDB to learn how to create a custom one:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> deepEqual </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'rxdb/plugins/utils'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> defaultConflictHandler</span><span class="token operator">:</span><span class="token plain"> RxConflictHandler</span><span class="token operator"><</span><span class="token builtin" style="color:rgb(189, 147, 249)">any</span><span class="token operator">></span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">isEqual</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">a</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> b</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token doc-comment comment" style="color:rgb(98, 114, 164)">/**</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token doc-comment comment" style="color:rgb(98, 114, 164)"> * isEqual() is used to detect conflicts or to detect if a</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token doc-comment comment" style="color:rgb(98, 114, 164)"> * document has to be pushed to the remote.</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token doc-comment comment" style="color:rgb(98, 114, 164)"> * If the documents are deep equal,</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token doc-comment comment" style="color:rgb(98, 114, 164)"> * we have no conflict.</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token doc-comment comment" style="color:rgb(98, 114, 164)"> * Because deepEqual is CPU expensive, on your custom conflict handler you might only</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token doc-comment comment" style="color:rgb(98, 114, 164)"> * check some properties, like the updatedAt time or revisions</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token doc-comment comment" style="color:rgb(98, 114, 164)"> * for better performance.</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token doc-comment comment" style="color:rgb(98, 114, 164)"> */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">deepEqual</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">a</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> b</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">resolve</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">i</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token doc-comment comment" style="color:rgb(98, 114, 164)">/**</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token doc-comment comment" style="color:rgb(98, 114, 164)"> * The default conflict handler will always</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token doc-comment comment" style="color:rgb(98, 114, 164)"> * drop the fork state and use the master state instead.</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token doc-comment comment" style="color:rgb(98, 114, 164)"> * </span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token doc-comment comment" style="color:rgb(98, 114, 164)"> * In your custom conflict handler you likely want to merge properties</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token doc-comment comment" style="color:rgb(98, 114, 164)"> * of the realMasterState and the newDocumentState instead.</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token doc-comment comment" style="color:rgb(98, 114, 164)"> */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> i</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">realMasterState</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div>
<p>To overwrite the default conflict handler, you have to specify a custom <code>conflictHandler</code> property when creating a collection with <code>addCollections()</code>.</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> myCollections </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:rgb(189, 147, 249);font-style:italic">await</span><span class="token plain"> myDatabase</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token method function property-access" style="color:rgb(80, 250, 123)">addCollections</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token comment" style="color:rgb(98, 114, 164)">// key = collectionName</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token literal-property property">humans</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token literal-property property">schema</span><span class="token operator">:</span><span class="token plain"> mySchema</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token literal-property property">conflictHandler</span><span class="token operator">:</span><span class="token plain"> myCustomConflictHandler</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="Copy code to clipboard" title="Copy" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg viewBox="0 0 24 24" class="copyButtonIcon_y97N"><path fill="currentColor" d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg viewBox="0 0 24 24" class="copyButtonSuccessIcon_LjdS"><path fill="currentColor" d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div></div></article><nav class="pagination-nav docusaurus-mt-lg" aria-label="Docs pages"><a class="pagination-nav__link pagination-nav__link--prev" href="/rx-server-scaling.html"><div class="pagination-nav__sublabel">Previous</div><div class="pagination-nav__label">RxServer Scaling</div></a><a class="pagination-nav__link pagination-nav__link--next" href="/query-cache.html"><div class="pagination-nav__sublabel">Next</div><div class="pagination-nav__label">Query Cache</div></a></nav></div></div><div class="col col--3"><div class="tableOfContents_bqdL thin-scrollbar theme-doc-toc-desktop"><ul class="table-of-contents table-of-contents__left-border"><li><a href="#why-rxdb-does-not-have-transactions" class="table-of-contents__link toc-highlight">Why RxDB does not have transactions</a></li><li><a href="#revisions" class="table-of-contents__link toc-highlight">Revisions</a></li><li><a href="#conflicts" class="table-of-contents__link toc-highlight">Conflicts</a><ul><li><a href="#local-conflicts" class="table-of-contents__link toc-highlight">Local conflicts</a></li></ul></li><li><a href="#replication-conflicts" class="table-of-contents__link toc-highlight">Replication conflicts</a></li><li><a href="#custom-conflict-handler" class="table-of-contents__link toc-highlight">Custom conflict handler</a></li></ul></div></div></div></div></main></div></div></div><div class="block footer"><div class="footer-block"><div class="footer-links"><span><a variant="text" href="/" class="footer-logo-button"><img src="/files/logo/logo.svg" alt="RxDB" loading="lazy"><div>RxDB</div></a><div class="footer-community-links"><a variant="text" href="/chat/" target="_blank"><img src="/img/community-links/discord-logo.svg" alt="RxDB Discord" loading="lazy"></a><a variant="text" href="/code/" target="_blank"><img src="/img/community-links/github-logo.svg" alt="RxDB Github" loading="lazy"></a><a variant="text" href="https://twitter.com/intent/user?screen_name=rxdbjs" target="_blank"><img src="/img/community-links/x-logo.svg" alt="RxDB Twitter" loading="lazy"></a><a variant="text" href="https://www.linkedin.com/company/rxdb" target="_blank"><img src="/img/community-links/linkedin-logo.svg" alt="RxDB LinkedIn" loading="lazy"></a><a variant="text" href="https://stackoverflow.com/questions/tagged/rxdb" target="_blank"><img src="/img/community-links/stack-overflow-logo.svg" alt="RxDB Stack Overflow" loading="lazy"></a></div></span><div class="footer-nav-links"><a variant="text" href="/premium/" target="">Premium</a><a variant="text" href="/consulting/" target="">Support</a><a variant="text" href="/overview.html" target="">Documentation</a><a variant="text" href="/chat/" target="_blank">Discord</a><a variant="text" href="/code/" target="_blank">Github</a><a variant="text" href="https://twitter.com/intent/user?screen_name=rxdbjs" target="_blank">Twitter</a><a variant="text" href="https://www.linkedin.com/company/rxdb" target="_blank">LinkedIn</a></div></div><div class="footer-policy"><div><a variant="text" href="/legal-notice/" target="_blank">Legal Notice</a></div><span class="footer-rights">© 2025 RxDB. All rights reserved.</span></div><img class="footer-img desktop-img" src="/img/footer-column.svg" alt="columns" loading="lazy"></div></div><div class="call-to-action-popup"><div class="close"><div class="text">✕</div></div></div></div>
</body>
</html>