mirror of
https://github.com/laurent22/joplin.git
synced 2025-01-23 18:53:36 +02:00
390 lines
16 KiB
HTML
390 lines
16 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
|
|
<!--
|
|
|
|
!!! WARNING !!!
|
|
|
|
This file was auto-generated from readme/spec/sync_lock.md and any manual change
|
|
made to it will be overwritten. To make a change to this file please modify
|
|
the source Markdown file:
|
|
|
|
https://github.com/laurent22/joplin/blob/dev/readme/spec/sync_lock.md
|
|
|
|
-->
|
|
|
|
<head>
|
|
<meta
|
|
charset="utf-8"
|
|
http-equiv="X-UA-Compatible"
|
|
content="IE=edge,chrome=1"
|
|
/>
|
|
<link rel="icon" href="/images/favicon.png" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
<meta name="theme-color" content="#000000" />
|
|
<meta name="description" content="Joplin website" />
|
|
<link
|
|
rel="stylesheet"
|
|
href="/css/bootstrap5.0.2.min.css"
|
|
as="style"
|
|
/>
|
|
<link rel="stylesheet" href="/css/fontawesome-all.min.css?t=1626452079487">
|
|
<link
|
|
href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;500;600;700&display=swap"
|
|
rel="stylesheet"
|
|
as="style"
|
|
media="all"
|
|
onload="this.media='all'; this.onload = null"
|
|
/>
|
|
<link rel="stylesheet" href="/css/site.css?t=1626452079487" as="style" />
|
|
<title>Lock types | Joplin</title>
|
|
</head>
|
|
<body class="website-env-prod">
|
|
<div class="container-fluid generic-template -page" id="main-container">
|
|
|
|
<div class=" navbar-main white-bg" id="nav-section">
|
|
<div class="container">
|
|
<div class="row">
|
|
<div class="col-3">
|
|
<a href="/">
|
|
<img
|
|
src="/images/logo-text-blue.svg"
|
|
alt=""
|
|
id="top-logo"
|
|
width="180"
|
|
/>
|
|
</a>
|
|
</div>
|
|
<div class="col-9 text-right d-none d-md-block">
|
|
<a href="/help/" class="fw500">Help</a>
|
|
<a href="https://discourse.joplinapp.org/" class="fw500">Forum</a>
|
|
<a class="button-link btn-blue sponsor-button" href="/donate">
|
|
<i class="fas fa-heart heart-full"></i><i class="far fa-heart heart-line"></i> Support us
|
|
</a> </div>
|
|
<div class="col-9 text-right d-block d-md-none">
|
|
<span class="pointer"
|
|
><img
|
|
src="/images/mobile-menu-black-open-icon.png"
|
|
id="open-menu-mobile"
|
|
alt=""
|
|
/></span>
|
|
|
|
|
|
<div id="menu-mobile">
|
|
<div>
|
|
<div class="text-right">
|
|
<img
|
|
src="/images/close-icon.png"
|
|
alt=""
|
|
class="pointer"
|
|
id="close-menu-mobile"
|
|
/>
|
|
</div>
|
|
|
|
<div class="text-center menu-mobile-top">
|
|
<a href="https://discourse.joplinapp.org/" class="fw500 mobile-menu-link">Forum</a>
|
|
<a href="/help/" class="fw500 mobile-menu-link">Help</a>
|
|
</div>
|
|
|
|
<div class="menu-mobile-buttons">
|
|
<a class="button-link btn-blue sponsor-button" href="/donate">
|
|
<i class="fas fa-heart heart-full"></i><i class="far fa-heart heart-line"></i> Support us
|
|
</a> </div>
|
|
</div>
|
|
|
|
<div id="toc-mobile"><div><ul>
|
|
<li>
|
|
<p>Applications</p>
|
|
<ul>
|
|
<li><a href="/desktop/">Desktop application</a></li>
|
|
<li><a href="/mobile/">Mobile applications</a></li>
|
|
<li><a href="/terminal/">Terminal application</a></li>
|
|
<li><a href="/clipper/">Web Clipper</a></li>
|
|
</ul>
|
|
</li>
|
|
<li>
|
|
<p>Support</p>
|
|
<ul>
|
|
<li><a href="https://discourse.joplinapp.org">Joplin Forum</a></li>
|
|
<li><a href="/markdown/">Markdown Guide</a></li>
|
|
<li><a href="/e2ee/">How to enable end-to-end encryption</a></li>
|
|
<li><a href="/conflict/">What is a conflict?</a></li>
|
|
<li><a href="/debugging/">How to enable debug mode</a></li>
|
|
<li><a href="/rich_text_editor/">About the Rich Text editor limitations</a></li>
|
|
<li><a href="/faq/">FAQ</a></li>
|
|
</ul>
|
|
</li>
|
|
<li>
|
|
<p>Joplin API - Get Started</p>
|
|
<ul>
|
|
<li><a href="/api/overview/">Joplin API Overview</a></li>
|
|
<li><a href="/api/get_started/plugins/">Plugin development</a></li>
|
|
<li><a href="/api/tutorials/toc_plugin/">Plugin tutorial</a></li>
|
|
</ul>
|
|
</li>
|
|
<li>
|
|
<p>Joplin API - References</p>
|
|
<ul>
|
|
<li><a href="https://joplinapp.org/api/references/plugin_api/classes/joplin.html">Plugin API</a></li>
|
|
<li><a href="/api/references/rest_api/">Data API</a></li>
|
|
<li><a href="/api/references/plugin_manifest/">Plugin manifest</a></li>
|
|
<li><a href="/api/references/plugin_loading_rules/">Plugin loading rules</a></li>
|
|
<li><a href="/api/references/plugin_theming/">Plugin theming</a></li>
|
|
</ul>
|
|
</li>
|
|
<li>
|
|
<p>Development</p>
|
|
<ul>
|
|
<li><a href="https://github.com/laurent22/joplin/blob/dev/BUILD.md">How to build the apps</a></li>
|
|
<li><a href="/spec/e2ee/">End-to-end encryption spec</a></li>
|
|
<li><a href="/spec/history/">Note History spec</a></li>
|
|
<li><a href="/spec/sync_lock/">Sync Lock spec</a></li>
|
|
<li><a href="/spec/plugins/">Plugin Architecture spec</a></li>
|
|
<li><a href="/spec/search_sorting/">Search Sorting spec</a></li>
|
|
<li><a href="/spec/server_file_url_format/">Server: File URL Format</a></li>
|
|
<li><a href="/spec/server_delta_sync/">Server: Delta Sync</a></li>
|
|
<li><a href="/spec/server_sharing/">Server: Sharing</a></li>
|
|
</ul>
|
|
</li>
|
|
<li>
|
|
<p>Google Summer of Code 2021</p>
|
|
<ul>
|
|
<li><a href="/gsoc2021/index/">Google Summer of Code 2021</a></li>
|
|
<li><a href="/gsoc2021/pull_request_guidelines/">How to submit a GSoC pull request</a></li>
|
|
<li><a href="/gsoc2021/ideas/">Project Ideas</a></li>
|
|
</ul>
|
|
</li>
|
|
<li>
|
|
<p>About</p>
|
|
<ul>
|
|
<li><a href="/changelog/">Changelog (Desktop App)</a></li>
|
|
<li><a href="/changelog_cli/">Changelog (CLI App)</a></li>
|
|
<li><a href="/changelog_server/">Changelog (Server)</a></li>
|
|
<li><a href="/stats/">Stats</a></li>
|
|
<li><a href="/donate/">Donate</a></li>
|
|
</ul>
|
|
</li>
|
|
</ul>
|
|
</div></div>
|
|
|
|
<div>
|
|
<p class="light-blue mobile-menu-link-bottom text-center">
|
|
Copyright © 2016-2021 Laurent Cozic
|
|
<br/>
|
|
<a href="/privacy/" class="fw500">Privacy Policy</a>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="page-container page-sync_lock">
|
|
<div class="container">
|
|
<div class="row content-wrapper">
|
|
<div id="toc"><div><ul>
|
|
<li>
|
|
<p>Applications</p>
|
|
<ul>
|
|
<li><a href="/desktop/">Desktop application</a></li>
|
|
<li><a href="/mobile/">Mobile applications</a></li>
|
|
<li><a href="/terminal/">Terminal application</a></li>
|
|
<li><a href="/clipper/">Web Clipper</a></li>
|
|
</ul>
|
|
</li>
|
|
<li>
|
|
<p>Support</p>
|
|
<ul>
|
|
<li><a href="https://discourse.joplinapp.org">Joplin Forum</a></li>
|
|
<li><a href="/markdown/">Markdown Guide</a></li>
|
|
<li><a href="/e2ee/">How to enable end-to-end encryption</a></li>
|
|
<li><a href="/conflict/">What is a conflict?</a></li>
|
|
<li><a href="/debugging/">How to enable debug mode</a></li>
|
|
<li><a href="/rich_text_editor/">About the Rich Text editor limitations</a></li>
|
|
<li><a href="/faq/">FAQ</a></li>
|
|
</ul>
|
|
</li>
|
|
<li>
|
|
<p>Joplin API - Get Started</p>
|
|
<ul>
|
|
<li><a href="/api/overview/">Joplin API Overview</a></li>
|
|
<li><a href="/api/get_started/plugins/">Plugin development</a></li>
|
|
<li><a href="/api/tutorials/toc_plugin/">Plugin tutorial</a></li>
|
|
</ul>
|
|
</li>
|
|
<li>
|
|
<p>Joplin API - References</p>
|
|
<ul>
|
|
<li><a href="https://joplinapp.org/api/references/plugin_api/classes/joplin.html">Plugin API</a></li>
|
|
<li><a href="/api/references/rest_api/">Data API</a></li>
|
|
<li><a href="/api/references/plugin_manifest/">Plugin manifest</a></li>
|
|
<li><a href="/api/references/plugin_loading_rules/">Plugin loading rules</a></li>
|
|
<li><a href="/api/references/plugin_theming/">Plugin theming</a></li>
|
|
</ul>
|
|
</li>
|
|
<li>
|
|
<p>Development</p>
|
|
<ul>
|
|
<li><a href="https://github.com/laurent22/joplin/blob/dev/BUILD.md">How to build the apps</a></li>
|
|
<li><a href="/spec/e2ee/">End-to-end encryption spec</a></li>
|
|
<li><a href="/spec/history/">Note History spec</a></li>
|
|
<li><a href="/spec/sync_lock/">Sync Lock spec</a></li>
|
|
<li><a href="/spec/plugins/">Plugin Architecture spec</a></li>
|
|
<li><a href="/spec/search_sorting/">Search Sorting spec</a></li>
|
|
<li><a href="/spec/server_file_url_format/">Server: File URL Format</a></li>
|
|
<li><a href="/spec/server_delta_sync/">Server: Delta Sync</a></li>
|
|
<li><a href="/spec/server_sharing/">Server: Sharing</a></li>
|
|
</ul>
|
|
</li>
|
|
<li>
|
|
<p>Google Summer of Code 2021</p>
|
|
<ul>
|
|
<li><a href="/gsoc2021/index/">Google Summer of Code 2021</a></li>
|
|
<li><a href="/gsoc2021/pull_request_guidelines/">How to submit a GSoC pull request</a></li>
|
|
<li><a href="/gsoc2021/ideas/">Project Ideas</a></li>
|
|
</ul>
|
|
</li>
|
|
<li>
|
|
<p>About</p>
|
|
<ul>
|
|
<li><a href="/changelog/">Changelog (Desktop App)</a></li>
|
|
<li><a href="/changelog_cli/">Changelog (CLI App)</a></li>
|
|
<li><a href="/changelog_server/">Changelog (Server)</a></li>
|
|
<li><a href="/stats/">Stats</a></li>
|
|
<li><a href="/donate/">Donate</a></li>
|
|
</ul>
|
|
</li>
|
|
</ul>
|
|
</div></div>
|
|
|
|
<div class="main-content">
|
|
<div class="alert alert-danger alert-env-dev" role="alert">
|
|
Running in prod mode!
|
|
</div>
|
|
<div class="donate-links">
|
|
<p><a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=E8JMYD2LQ8MMA&lc=GB&item_name=Joplin+Development&currency_code=EUR&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted"><img src="https://joplinapp.org/images/badges/Donate-PayPal-green.svg" alt="Donate using PayPal"></a> <a href="https://github.com/sponsors/laurent22/"><img src="https://joplinapp.org/images/badges/GitHub-Badge.svg" alt="Sponsor on GitHub"></a> <a href="https://www.patreon.com/joplin"><img src="https://joplinapp.org/images/badges/Patreon-Badge.svg" alt="Become a patron"></a> <a href="https://joplinapp.org/donate/#donations"><img src="https://joplinapp.org/images/badges/Donate-IBAN.svg" alt="Donate using IBAN"></a></p>
|
|
</div>
|
|
<h1>Lock types<a name="lock-types" href="#lock-types" class="heading-anchor">🔗</a></h1>
|
|
<p>There are two types of locks:</p>
|
|
<ul>
|
|
<li><strong>SYNC</strong>: Used when synchronising a client with a target. There can be multiple SYNC locks simultaneously.</li>
|
|
<li><strong>EXCLUSIVE</strong>: Used when a client upgrades a sync target. There can be only one EXCLUSIVE lock.</li>
|
|
</ul>
|
|
<h1>Timeout<a name="timeout" href="#timeout" class="heading-anchor">🔗</a></h1>
|
|
<p>When a client acquires a lock, it must refresh it every X seconds. A lock timeout after Y seconds (where X < Y). A lock with a timestamp greater than Y is considered expired and can be ignored by other clients. A client that tries to refresh a lock that has expired should fail.</p>
|
|
<p>For example, if a client is currently syncing, it must stop doing so if it couldn't refresh the lock with Y seconds.</p>
|
|
<p>For example, if a client is upgrading a target, it must stop doing so if it couldn't refresh the lock within Y seconds.</p>
|
|
<p>If the previous lock has expired, we shouldn't try to acquire a new one. This is because other clients, seeing no active lock, might have performed in the meantime operations that invalidates the current operation. For example, another client might have upgraded the sync target, so any active sync with an expired lock should be cancelled. Or if the current client was upgrading the sync target, another client might have synced since then, making any cached data invalid.</p>
|
|
<p>In some cases it should be safe to re-acquire a lock but adding support for this would make the algorithm more complex without much benefits.</p>
|
|
<h1>Acquiring a SYNC lock<a name="acquiring-a-sync-lock" href="#acquiring-a-sync-lock" class="heading-anchor">🔗</a></h1>
|
|
<ul>
|
|
<li>The client check if there is a valid EXCLUSIVE lock on the target</li>
|
|
<li>If there is, it must stop the sync process</li>
|
|
<li>Otherwise it checks if it owns a SYNC lock on the target
|
|
<ul>
|
|
<li>If it does, it starts syncing
|
|
<ul>
|
|
<li>When syncing is done, it releases the SYNC lock</li>
|
|
</ul>
|
|
</li>
|
|
<li>If it doesn't, it acquires a SYNC lock and repeat the complete process from the beginning (to avoid race conditions)</li>
|
|
</ul>
|
|
</li>
|
|
</ul>
|
|
<h1>Acquiring an EXCLUSIVE lock<a name="acquiring-an-exclusive-lock" href="#acquiring-an-exclusive-lock" class="heading-anchor">🔗</a></h1>
|
|
<ul>
|
|
<li>The client check if there is a valid EXCLUSIVE or SYNC lock on the target</li>
|
|
<li>If there is, it must stop the upgrade process (or wait till target is unlocked)</li>
|
|
<li>Otherwise it checks if it owns an EXCLUSIVE lock on the target
|
|
<ul>
|
|
<li>If it does, it starts upgrading the target
|
|
<ul>
|
|
<li>When upgrading is done, it releases the EXCLUSIVE lock</li>
|
|
</ul>
|
|
</li>
|
|
<li>If it doesn't, it acquires an EXCLUSIVE lock and repeat the complete process from the beginning (to avoid race conditions)</li>
|
|
</ul>
|
|
</li>
|
|
</ul>
|
|
<h1>Lock files<a name="lock-files" href="#lock-files" class="heading-anchor">🔗</a></h1>
|
|
<p>The lock files are in format <code><lockType>_<clientType>_<clientId>.json</code> with lockType being "exclusive" or "sync", clientType being "desktop", "mobile" or "cli" and clientId is the globally unique ID assigned to a client profile when it is created.</p>
|
|
<p>The have the following content:</p>
|
|
<pre><code class="language-json">{
|
|
"type": "exclusive",
|
|
"clientType": <string>,
|
|
"clientId": <string>,
|
|
"updatedTime": <timestamp in milliseconds>,
|
|
}
|
|
</code></pre>
|
|
<p>(Note that the lock file content is for information purpose only. Its content is not used in the lock algorithm since all data can be derived from the filename and file timestamp)</p>
|
|
<p>Although only one client can acquire an exclusive lock, there can be multiple <code>exclusive_*.json</code> lock files in the lock folder (for example if a client crashed before releasing a lock or if two clients try to acquire a lock at the exact same time). In this case, only the oldest lock amongst the active ones is the valid one. If there are two locks with the same timestamp, the one with lowest client ID is the valid one.</p>
|
|
<h1>Sync Target Migration<a name="sync-target-migration" href="#sync-target-migration" class="heading-anchor">🔗</a></h1>
|
|
<p>First the app checks the sync target version - if it's new (no version), it set it up by upgrading to the latest sync version.</p>
|
|
<p>If it's the same as the client supported version, it syncs as normal.</p>
|
|
<p>If it's lower than the client supported version, the client does not allow sync and instead displays a message asking the user to upgrade the sync target (upgradeState = SHOULD_UPGRADE).</p>
|
|
<p>If the user click on the link to upgrade, upgradeState becomes MUST_UPGRADE, and the app restarts.</p>
|
|
<p>On startup, the app check the upgradeState setting. If it is MUST_UPGRADE it displays the upgrade screen and starts upgrarding. Once done it sets upgradeState back to IDLE, and restart the app.</p>
|
|
|
|
<div class="bottom-links">
|
|
<a href="https://github.com/laurent22/joplin/blob/dev/readme/spec/sync_lock.md">
|
|
<i class="fab fa-github"></i> Improve this doc
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<footer class="darkblue-bg">
|
|
<div class="container">
|
|
<div class="row">
|
|
<div class="col-3 d-none d-md-block">
|
|
<img src="/images/logo-text.svg" alt="" width="150" />
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<hr />
|
|
</div>
|
|
</div>
|
|
<div class="row">
|
|
<div class="col-12 col-md-6">
|
|
<img
|
|
src="/images/logo-text.svg"
|
|
width="120"
|
|
class="img-center d-block d-md-none"
|
|
alt=""
|
|
/>
|
|
<br class="d-block d-md-none" />
|
|
<p class="text-center-sm">Copyright © 2016-2021 Laurent Cozic</p>
|
|
</div>
|
|
<div class="col-12 col-md-6 right-links">
|
|
<p class="text-right text-center-sm">
|
|
<a href="https://github.com/laurent22/joplin/" class="github-link"><i class="fab fa-github"></i> GitHub Repository</a>
|
|
<a href="/privacy/">Privacy Policy</a>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</footer>
|
|
</div>
|
|
|
|
<script
|
|
src="/js/jquery-3.6.0.min.js"
|
|
rel="preload"
|
|
as="script"
|
|
></script>
|
|
<script src="/js/script.js?t=1626452079487"></script>
|
|
|
|
<script>
|
|
if (window.location.hostname !== 'localhost') {
|
|
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
|
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
|
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
|
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
|
|
ga('create', 'UA-103586105-1', 'auto');
|
|
ga('send', 'pageview');
|
|
}
|
|
</script> </body>
|
|
</html>
|