{"componentChunkName":"component---src-templates-post-template-tsx","path":"/pl/distributed-locking-a-practical-guide/","result":{"data":{"locales":{"edges":[{"node":{"language":"en","ns":"translation","data":"{\"404\":{\"title\":\"Not found\",\"description\":\"You just hit a route that doesn't exist... the sadness.\"},\"header\":{\"title\":\"Oskar Dudycz\",\"subTitle\":\"Pragmatic about programming\"},\"seo\":{\"siteDescription\":\"Practical articles, training, and consulting about Event Sourcing, event-driven architecture, CQRS, and software architecture.\"},\"menu\":{\"articles\":\"Articles\",\"categories\":\"Categories\",\"consulting\":\"Consulting\",\"contact\":\"Contact\",\"talks\":\"Talks\",\"training\":\"Training\",\"about\":\"About\",\"search\":\"Search\",\"more\":\"More links\"},\"hero\":{\"h1\":\"Are you looking for practical knowledge <linebreak /> about <architecture>software architecture?</architecture>\",\"h2\":\"Check out my content - <formats>from articles to videos</formats>\",\"introExperience\":\"My name is Oskar Dudycz. For more than 18 years, I've built software close to the business.\",\"introTraining\":\"I run workshops and training about Event Sourcing, CQRS, and event-driven architecture.\",\"introEmmett\":\"I maintain <emmett>Emmett</emmett>, an open-source library for implementing Event Sourcing.\",\"introBlog\":\"On this blog, I share practical ways to build well-designed, modular applications.\",\"servicesLabel\":\"Professional services\",\"trainingCta\":\"Explore training\",\"consultingCta\":\"Discuss consulting\",\"articlesCta\":\"Read latest articles\"},\"blog\":{\"browseAll\":\"Browse all articles\",\"latestTitle\":\"Latest articles\",\"allTitle\":\"All articles\",\"allIntro\":\"Browse the complete archive, from the newest article back to the earliest posts.\"},\"categories\":{\"title\":\"Recommended reading by topic\",\"intro\":\"Choose a topic to follow a recommended learning path or browse all related articles.\",\"topic\":\"Topic\",\"recommended\":\"Recommended reading order\",\"recommendedDescription\":\"Read these in order to move from the foundations to practical trade-offs and production concerns.\",\"moreArticles\":\"More articles\",\"articles\":\"Articles\",\"articleCount\":\"{{count}} article\",\"articleCount_plural\":\"{{count}} articles\",\"defaultDescription\":\"Practical articles about {{category}}.\",\"seoTitle\":\"{{category}} articles and learning path\",\"seoTitleAll\":\"Software architecture topics and reading paths\",\"seoDescription\":\"Recommended reading paths and practical articles about Event Sourcing, CQRS, event-driven architecture, and software design.\"},\"talks\":{\"title\":\"Talks and videos\",\"videosTitle\":\"Selected videos\",\"videosIntro\":\"Conference talks, webinars, and practical sessions about Event Sourcing, CQRS, event-driven architecture, and software design.\",\"watch\":\"Watch recording\",\"play\":\"Play {{title}}\",\"titleLabel\":\"Title\",\"descriptionLabel\":\"Description\"},\"related\":{\"title\":\"Related articles\"},\"articleNavigation\":{\"title\":\"Articles by publication date\",\"later\":\"Later article\",\"earlier\":\"Earlier article\"},\"contact\":{\"title\":\"Contact\",\"seoTitle\":\"Contact Oskar Dudycz\",\"description\":\"Contact Oskar Dudycz about Event Sourcing training, software architecture consulting, and speaking.\",\"intro\":\"For consulting, architecture reviews, or training,\",\"bookCall\":\"book a free introductory call.\"},\"appearance\":{\"label\":\"Theme\",\"system\":\"System\",\"light\":\"Light\",\"dark\":\"Dark\"}}"}},{"node":{"language":"pl","ns":"translation","data":"{\"404\":{\"title\":\"Nie znaleziono\",\"description\":\"Właśnie trafiłeś na URL, który nie istnieje... smuteczek.\"},\"header\":{\"title\":\"Oskar Dudycz\",\"subTitle\":\"Pragmatycznie o programowaniu\"},\"seo\":{\"siteDescription\":\"Praktyczne artykuły, szkolenia i konsultacje o Event Sourcingu, architekturze sterowanej zdarzeniami, CQRS i architekturze oprogramowania.\"},\"menu\":{\"articles\":\"Artykuły\",\"categories\":\"Kategorie\",\"consulting\":\"Konsulting\",\"contact\":\"Kontakt\",\"talks\":\"Wystąpienia\",\"training\":\"Szkolenia\",\"about\":\"O mnie\",\"search\":\"Szukaj\",\"more\":\"Więcej linków\"},\"hero\":{\"h1\":\"Szukasz praktycznej wiedzy <linebreak /> o <architecture>architekturze oprogramowania?</architecture>\",\"h2\":\"Zapoznaj się z moimi treściami - <formats>od artykułów po wideo</formats>\",\"introExperience\":\"Nazywam się Oskar Dudycz. Od ponad 18 lat tworzę systemy informatyczne bliskie biznesowi.\",\"introTraining\":\"Prowadzę warsztaty i szkolenia o Event Sourcingu, CQRS-ie oraz architekturze opartej na zdarzeniach.\",\"introEmmett\":\"Rozwijam <emmett>Emmett</emmett>, bibliotekę open source ułatwiającą wdrażanie Event Sourcingu.\",\"introBlog\":\"Na blogu pokazuję, jak pragmatycznie budować dobrze zaprojektowane, modularne aplikacje.\",\"servicesLabel\":\"Usługi profesjonalne\",\"trainingCta\":\"Zobacz szkolenia\",\"consultingCta\":\"Porozmawiajmy o konsultingu\",\"articlesCta\":\"Przeczytaj najnowsze artykuły\"},\"blog\":{\"browseAll\":\"Zobacz wszystkie artykuły\",\"latestTitle\":\"Najnowsze artykuły\",\"allTitle\":\"Wszystkie artykuły\",\"allIntro\":\"Przejrzyj pełne archiwum — od najnowszego artykułu aż po najstarsze wpisy.\"},\"categories\":{\"title\":\"Polecana kolejność czytania według tematów\",\"intro\":\"Wybierz temat, aby przejść polecaną ścieżkę nauki albo zobaczyć wszystkie powiązane artykuły.\",\"topic\":\"Temat\",\"recommended\":\"Polecana kolejność czytania\",\"recommendedDescription\":\"Czytaj w tej kolejności, aby przejść od podstaw do praktycznych kompromisów i problemów produkcyjnych.\",\"moreArticles\":\"Więcej artykułów\",\"articles\":\"Artykuły\",\"articleCount_0\":\"{{count}} artykuł\",\"articleCount_1\":\"{{count}} artykuły\",\"articleCount_2\":\"{{count}} artykułów\",\"defaultDescription\":\"Praktyczne artykuły na temat {{category}}.\",\"seoTitle\":\"{{category}} — artykuły i ścieżka nauki\",\"seoTitleAll\":\"Tematy i ścieżki nauki architektury oprogramowania\",\"seoDescription\":\"Polecane ścieżki czytania i praktyczne artykuły o Event Sourcingu, CQRS-ie, architekturze opartej na zdarzeniach i projektowaniu oprogramowania.\"},\"talks\":{\"title\":\"Wystąpienia i nagrania\",\"videosTitle\":\"Wybrane nagrania\",\"videosIntro\":\"Wystąpienia konferencyjne, webinary i praktyczne sesje o Event Sourcingu, CQRS-ie, architekturze opartej na zdarzeniach i projektowaniu oprogramowania.\",\"watch\":\"Obejrzyj nagranie\",\"play\":\"Odtwórz {{title}}\",\"titleLabel\":\"Tytuł\",\"descriptionLabel\":\"Opis\"},\"related\":{\"title\":\"Powiązane artykuły\"},\"articleNavigation\":{\"title\":\"Artykuły według daty publikacji\",\"later\":\"Nowszy artykuł\",\"earlier\":\"Starszy artykuł\"},\"contact\":{\"title\":\"Kontakt\",\"seoTitle\":\"Skontaktuj się z Oskarem Dudyczem\",\"description\":\"Skontaktuj się z Oskarem Dudyczem w sprawie szkoleń z Event Sourcingu, konsultingu architektonicznego i wystąpień.\",\"intro\":\"W sprawie konsultingu, przeglądu architektury lub szkolenia\",\"bookCall\":\"umów bezpłatną rozmowę wstępną.\"},\"appearance\":{\"label\":\"Motyw\",\"system\":\"Systemowy\",\"light\":\"Jasny\",\"dark\":\"Ciemny\"}}"}}]},"relatedPosts":{"edges":[]},"post":{"id":"ec171849-8363-58ed-9b65-2f09137ba7a4","html":"<p><strong>Was your data ever mysteriously overwritten? No? Think again. Have you noticed conflicting updates to the same data?</strong> Still nope? Lucky you! I had cases when my data changed, and it looked like Marty was travelling back to the future. Once it was correct, then it was wrong, then correct again. Most of the time, twin services were writing to the sample place.</p>\n<p><strong>In distributed systems, coordination is crucial.</strong> Whenever you have multiple processes (or services) that could update or read the same data simultaneously, you risk data corruption, race conditions, or unwanted duplicates. The popular solution is to use distributed locks - a mechanism ensuring only one process can operate on a resource at a time.</p>\n<p><strong>A distributed lock ensures that if one actor (node, service instance, etc.) changes a shared resource—like a database record, file, or external service—no other node can step in until the first node is finished.</strong> While the concept is straightforward, implementing it across multiple machines requires careful design and a robust failure strategy.</p>\n<p>Today, we’ll try to discuss it and:</p>\n<ol>\n<li>\n<p><strong>Explain</strong> why distributed locking is needed in real-world scenarios.</p>\n</li>\n<li>\n<p><strong>Explore</strong> how popular tools and systems implement locks (Redis, ZooKeeper, databases, Kubernetes single-instance setups, etc.).</p>\n</li>\n<li>\n<p><strong>Discuss</strong> potential pitfalls—like deadlocks, lock expiration, and single points of failure—and how to address them.</p>\n</li>\n</ol>\n<p>By the end, you should have a decent grasp of distributed locks, enough to make informed decisions about whether (and how) to use them in your architecture.</p>\n<hr>\n<h2 id=\"1-why-distributed-locks-matter\" style=\"position:relative;\"><a href=\"#1-why-distributed-locks-matter\" aria-label=\"1 why distributed locks matter permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>1. Why Distributed Locks Matter</h2>\n<p>When you scale an application to multiple machines or microservices, each one might update the same resource simultaneously. That can lead to writers overwriting each other. It’s the classic concurrency problem: <strong>who’s in charge of updating shared data?</strong> Without a strategy to coordinate these updates, you risk inconsistent results.</p>\n<p>For example, you might have:</p>\n<ul>\n<li>\n<p><strong>Multiple Writers are updating the same table row.</strong> One process believes it has the latest data and changes, but another process overwrites it moments later.<br>\n<strong>That’s a common issue when updating read models.</strong> We need to ensure a single subscriber for a specific read model. When multiple microservices listen to an event stream but only one should update a secondary database, a lock ensures they don’t collide.</p>\n</li>\n<li>\n<p><strong>At-Least-Once Messaging</strong> leading to repeated deliveries of the same message, with multiple consumers each processing it as if it were unique.</p>\n</li>\n<li>\n<p><strong>A Global Resource</strong> that can only support one writer at a time, such as a large file or an external API endpoint.</p>\n</li>\n<li>\n<p><strong>Scheduled jobs:</strong> If you schedule a job on multiple nodes to ensure reliability, you still want only the first node that grabs the lock to run it, preventing duplicates.</p>\n</li>\n</ul>\n<p>A <strong>distributed lock</strong> is the simplest way to say, “Only one node can modify this resource until it’s finished.” Other nodes wait or fail immediately rather than attempting parallel writes.</p>\n<p><strong>It can also be seen as</strong> <em><strong><a href=\"https://en.wikipedia.org/wiki/Leader_election\">Leader Election Lite</a></strong></em><strong>:</strong> If you only need “one node at a time” for a particular task, a plain lock is sometimes enough - there is no need for a full-blown leader election framework.</p>\n<p>Without locks, you can get unpredictable states - like a read model flipping from correct to incorrect or a file partially overwritten by multiple workers. Locks sacrifice a bit of parallelism for the certainty that no two nodes update the same resource simultaneously. In many cases, that’s the safest trade-off, especially if data correctness is paramount.</p>\n<p>There are other ways to handle concurrency (like <a href=\"/en/idempotent_command_handling/\">idempotent actions</a> or <a href=\"/en/the-write-ahead-log-a-foundation/\">write-ahead logs</a>). Still, a distributed lock is often the most direct solution when you truly need to avoid simultaneous writes.</p>\n<h2 id=\"2-how-locks-typically-work\" style=\"position:relative;\"><a href=\"#2-how-locks-typically-work\" aria-label=\"2 how locks typically work permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>2. How Locks Typically Work</h2>\n<ol>\n<li>\n<p><strong>Lock Request</strong><br>\nA node determines it needs exclusive access to a shared resource and communicates with a lock manager (e.g., Redis, ZooKeeper, or a database) that supports TTL or ephemeral locks.</p>\n</li>\n<li>\n<p><strong>Acquire Attempt</strong></p>\n<ul>\n<li>\n<p>If no lock currently exists, the node creates one, for instance by setting a Redis key with a time-to-live or by creating an ephemeral zNode in ZooKeeper.</p>\n</li>\n<li>\n<p>If another node is holding the lock, this node either waits, fails immediately, or retries, depending on your chosen policy.</p>\n</li>\n</ul>\n</li>\n<li>\n<p><strong>Critical Operation</strong><br>\nAfter acquiring the lock, the node proceeds with the operation that must not be run in parallel—such as updating a database row, writing to a shared file, or sending a limited external API request.</p>\n</li>\n<li>\n<p><strong>Standard Release</strong><br>\nOnce finished, the node explicitly removes the lock record (e.g., deleting the Redis key or calling an unlock function). At this point, other contenders are free to acquire the lock.</p>\n</li>\n<li>\n<p><strong>Crash or Automatic Release</strong><br>\nIf the node fails or loses its session, the TTL expires, or the ephemeral mechanism detects the node’s absence. The lock manager then removes the lock, automatically merging into the same path as a standard release so a new node can acquire it. This avoids leaving the system stuck with a “zombie” lock that never gets freed.</p>\n</li>\n</ol>\n<p>The basic flow would look like:</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 800px; height: auto\"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/fb28a3149c35ba02ce9102df4a9c61c4/7f529/2024-12-23-cover.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 96.50000000000001%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAATCAYAAACQjC21AAAACXBIWXMAAAsTAAALEwEAmpwYAAACQ0lEQVR42pVU2XLbMBDz/39e+5Y6iQ85knXwvmRnimKp2o3taSZ5wHBEkdAC2NXqlCM+Yo4RVp1wbD0Oe41DY9BwPbYBejojh9vz91jdbxQSysWuK9huLH7+2OPlWaFrM4b+N7JPOJdvEAoSqyihkLzAW19XQSLZnL5Z4bkkeJ2gxwUt5ao+YiL0mL9LmOiRRXdIlOnx62la8GvCmnhrWOlXPczBITiD6A0CZYpX2TOkwKpiZtW2BvI+56riU8I5BUSSXSQv4J7NGI8Ow1GzugntYULfWagh/Dft1ZKsp+H25suSpJkSdpuhknVvBkfi0IzoW8M7mefCVwkT99h348h9X59PiVXLSnhj4JTCe3kM6YFwJpFVmU09otn2lOxrby4XAoyasd0GPDOkZkdvTan2fFphMJE+WbaKgZ3CtQqxIdDXvotsdIexz7yXbxr9gVDMntnEwfiKU5KG5uEUl3d8lvTNqGnBzDbKN610RxjpjYycwuZlwOZ1YCgTZcuEJKY943k94fVFcR3Zo3LGVhsuAa2EZE5CaNhjiZIcpWp64xnKskYXq+xgE/0Vj+Wd5ergdKgjea0w2Hg9NLLf9GhqpckVPsslUZCu/nlbYDmafac5CDMcQ4kfCdVQsN9pptZS6sTqSk16OJ6xXhvstqlekiT77oynJ00ojqLmSCrKl9/a6Z/k5OTLglClieF1FPnV6BZc2iZd9wKlOoZ0+/7q4T0uLfLx+bInq/yFmv30twe/8D/8DPJH7zuxSYKZH0bvD2VstaT5I3yhAAAAAElFTkSuQmCC'); background-size: cover; display: block;\"\n  ></span>\n  <picture>\n          <source\n              srcset=\"/static/fb28a3149c35ba02ce9102df4a9c61c4/507e6/2024-12-23-cover.webp 200w,\n/static/fb28a3149c35ba02ce9102df4a9c61c4/28a80/2024-12-23-cover.webp 400w,\n/static/fb28a3149c35ba02ce9102df4a9c61c4/8d2ea/2024-12-23-cover.webp 800w,\n/static/fb28a3149c35ba02ce9102df4a9c61c4/fb185/2024-12-23-cover.webp 825w\"\n              sizes=\"(max-width: 800px) 100vw, 800px\"\n              type=\"image/webp\"\n            />\n          <source\n            srcset=\"/static/fb28a3149c35ba02ce9102df4a9c61c4/36ca5/2024-12-23-cover.png 200w,\n/static/fb28a3149c35ba02ce9102df4a9c61c4/a3397/2024-12-23-cover.png 400w,\n/static/fb28a3149c35ba02ce9102df4a9c61c4/a331c/2024-12-23-cover.png 800w,\n/static/fb28a3149c35ba02ce9102df4a9c61c4/7f529/2024-12-23-cover.png 825w\"\n            sizes=\"(max-width: 800px) 100vw, 800px\"\n            type=\"image/png\"\n          />\n          <img\n            class=\"gatsby-resp-image-image\"\n            src=\"/static/fb28a3149c35ba02ce9102df4a9c61c4/a331c/2024-12-23-cover.png\"\n            alt=\"Distributed lock acquisition: create a lock record, check whether another owner holds it, retry or give up, execute the critical operation and release the lock.\"\n            title=\"\"\n            loading=\"lazy\"\n            decoding=\"async\"\n            style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n          />\n        </picture>\n  </a>\n    </span></p>\n<p>The general lock acquisition workflow</p>\n<p>And the acquisition part with TTL handling:</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 739px; height: auto\"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/05cc8f908bb232448dc69ed8f1a13c02/7b99c/image-2.png\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 107.5%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAWCAYAAADAQbwGAAAACXBIWXMAAAsTAAALEwEAmpwYAAAC+klEQVR42o1V23baMBDM/39fc2kgBAgYG1uydbfspNNZOWlOm9DDwx5Z0mrYnZ1dbqYU8K/5PqBvE9raomsc9Dlw9dx7dHXkPmH0X9+J3Xw99HB9wtBn7PdnrB73uP3xhPu7Lfa7Dl2XYPoZydF3vAJwpjlGqLsMo2dGFXDY9aheDHSb0XcTIxyR/JWAYjkGjMEXm1NCMAbBWkwx8my5++7dRUBJx/TkqrU0h9NRoTn1jM7TLNyQCvDVHAaboLpIzlrsdmesVwfstjUOL4rWwQ7vHF4DOJMXPwSodizkO/OKuhoYaYLt38jrBHWW1ONlQAF5zR5vk3x7eKNJuinnOQpf8Q+vyTsCa0RnLwOKTHQ3ouMvV4cB9VEqGqm9iIEy+a6abtDfA0pEbZ3x/Bxwf9/i4aHDej3g9rYmdxbn01Si/iJ+01+OUDizOnKlaBUrrCTtVCIP5pPbxfx74XRZZf9x98khL15zJIeJ3Jhib9NIp1geiU+wofxIdLmYanryOZVvOQ92KdKNEN2rjOOLZzU9V4vD3lB7spdqT8jFZ0LTvJEOTWpqPD6SHq7rdVfOdfdaCnizOM943mg8rVpsnhS5a/HzocbTWlEicwHUrQACp9OM1arH3V1Dnk84HjOaM1jQeQH8aLU5J6aZy3fm2a95Ig0jwcJ7ypGCJq9mZIq5dI10jOfeDeNnyqU6RJaHc5LCDMVEezKiJDqRTTQO0Yof285FnKumrNkvoyw5/wHoi9YqcnfcW+yYulh1sDjRVDMWZ6MSOvb19rnCdlORjgP22wYbrnJuOe6ScwugOlOHG0Wnhg86miKX7OOtJndzAdQU/aBHmhTKE6hCK8OXPd/rjEFJpO8cBsNRxdBzGMu0ttpxL5GNTHPRmMzIQHnImTcytQ19I8IgPR7L/V+9vOhtEWl0Pclnv/qBANKzqThH2/OOHFuFMVoWTYoZ/qwX52GpfOlxzf8QQz51+T9pq7YMjSWIeP2A/RxlTImy6JqBHMm0GfE/fwH8DUCZmNLF2H4sAAAAAElFTkSuQmCC'); background-size: cover; display: block;\"\n  ></span>\n  <picture>\n          <source\n              srcset=\"/static/05cc8f908bb232448dc69ed8f1a13c02/507e6/image-2.webp 200w,\n/static/05cc8f908bb232448dc69ed8f1a13c02/28a80/image-2.webp 400w,\n/static/05cc8f908bb232448dc69ed8f1a13c02/f349b/image-2.webp 739w\"\n              sizes=\"(max-width: 739px) 100vw, 739px\"\n              type=\"image/webp\"\n            />\n          <source\n            srcset=\"/static/05cc8f908bb232448dc69ed8f1a13c02/36ca5/image-2.png 200w,\n/static/05cc8f908bb232448dc69ed8f1a13c02/a3397/image-2.png 400w,\n/static/05cc8f908bb232448dc69ed8f1a13c02/7b99c/image-2.png 739w\"\n            sizes=\"(max-width: 739px) 100vw, 739px\"\n            type=\"image/png\"\n          />\n          <img\n            class=\"gatsby-resp-image-image\"\n            src=\"/static/05cc8f908bb232448dc69ed8f1a13c02/7b99c/image-2.png\"\n            alt=\"A lock with a time-to-live permits another owner to acquire it after the holder crashes or the lease expires.\"\n            title=\"\"\n            loading=\"lazy\"\n            decoding=\"async\"\n            style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n          />\n        </picture>\n  </a>\n    </span></p>\n<h2 id=\"3-tools-for-distributed-locking\" style=\"position:relative;\"><a href=\"#3-tools-for-distributed-locking\" aria-label=\"3 tools for distributed locking permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>3. Tools for Distributed Locking</h2>\n<p>There are many tools for distributed locking; let’s check the most popular for certain categories.</p>\n<ul>\n<li>\n<p><strong><a href=\"https://redis.io/\">Redis</a></strong> (and clones): You store a “lock key” in Redis with a time limit attached. If the lock key doesn’t exist, you create it and claim the lock. Once your task finishes, you remove the key. If your process crashes, Redis removes the key after the time limit expires, so no one stays locked out forever. This method is quick and works well if you already rely on Redis, but you should be cautious about network splits or cluster issues.</p>\n</li>\n<li>\n<p><strong><a href=\"https://zookeeper.apache.org/\">ZooKeeper</a> / <a href=\"https://etcd.io/\">etcd</a></strong>: These tools consistently keep data across a group of servers. You write a small record saying, “I own this lock,” and if you go offline, the system notices and removes your record automatically. ZooKeeper and etcd are often used for more advanced cluster coordination or leader election, so they’re more complex to set up than Redis. However, they offer strong consistency guarantees.</p>\n</li>\n<li>\n<p><strong>Database Locks</strong>: If you have a single database (like Postgres or MySQL) where all your data lives, you can let the database control concurrency. You request a named lock, and the database blocks any other request for that same lock name until you release it. It’s convenient if you already use a single DB for everything, but it may not scale well across multiple databases or regions, and frequent locking could cause contention.</p>\n</li>\n<li>\n<p><strong>Kubernetes Single-Instance</strong>: Instead of coordinating multiple replicas, you literally run only one replica of your service. Because there’s only one service process, you avoid concurrency altogether at the application level. It’s the simplest approach - no lock code is needed. The downside is that you lose parallel processing and can’t spin up more replicas for high availability or scaling.</p>\n</li>\n</ul>\n<p>Distributed locks all share a common goal: ensure only one node does a particular thing at any given time. However, each tool mentioned approaches the problem with distinct designs, strengths, and failover behaviours.</p>\n<p>Let’s look at each tool’s big-picture purpose—why you’d even consider it—then move on to how it implements (or approximates) a lock. Lastly, let’s discuss a few technical details that matter once you start coding or troubleshooting.</p>\n<h3 id=\"31-redis-in-memory-data-store-with-ttl-based-locking\" style=\"position:relative;\"><a href=\"#31-redis-in-memory-data-store-with-ttl-based-locking\" aria-label=\"31 redis in memory data store with ttl based locking permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>3.1 Redis (In-Memory Data Store with TTL-Based Locking)</h3>\n<p><strong>Redis runs all commands in a single thread, so there is no risk of two commands interfering partway through.</strong> It provides atomic commands to “claim” a key only if it doesn’t exist and supports a time-to-live (TTL). Redis allows multiple nodes to coordinate around a shared key space. Essentially, if one node sets a “lock key” successfully, that key signals to everyone else that the lock is taken.</p>\n<p><strong>Redis runs all commands in a single thread, so there is no risk of two commands interfering partway through.</strong> If two clients try to create the same lock key simultaneously, Redis processes them in strict sequence: either the first wins or the second does, but never both.</p>\n<p>To create a lock, you need to create a “lock key” using a Redis command, for instance:</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">SET lockKey node123 NX EX 30</code></pre></div>\n<p>That looks cryptic, but let’s see what’s happening behind the scenes:</p>\n<ol>\n<li>\n<p><strong>Atomic Key Creation with NX.</strong> The <strong>NX</strong> option in the <code class=\"language-text\">SET</code> command means “set the key only if it does not already exist.” Redis checks the key’s existence and creates it if none is found. This is all done atomically, so if another client tries the same operation, one will succeed, and the other will fail.</p>\n</li>\n<li>\n<p><strong>Time-to-Live with EX.</strong> The <strong>EX</strong> option (short for “expire after X seconds”) tells Redis to remove the key automatically after the specified number of seconds. If the client (the lock holder) crashes, once the EX timer hits zero, Redis deletes the key. This prevents the lock from staying locked forever.</p>\n</li>\n<li>\n<p><strong>Lock Ownership.</strong> When a client sets the key (for example, <code class=\"language-text\">SET lockKey node123 NX EX 30</code>), if Redis replies with <strong>OK</strong>, that client is considered to “own” the lock. If Redis replies with <strong>nil</strong> or an empty response, it means another client already owns it.</p>\n</li>\n<li>\n<p><strong>Renewals.</strong> If you need more time, say your operation runs longer than the initial 30 seconds, you can extend the lock by calling something like <code class=\"language-text\">PEXPIRE lockKey newTTL</code>, which resets the timer. Alternatively, you could run another <code class=\"language-text\">SET lockKey node123 NX EX 30</code> if you do it carefully (although the recommended pattern is to check that you still own the lock before renewing).</p>\n</li>\n<li>\n<p><strong>Releasing the Lock.</strong> Once finished, the client <strong>deletes</strong> the key, typically with <code class=\"language-text\">DEL lockKey</code>. This frees the lock immediately so other nodes can acquire it. It’s crucial to verify that you are indeed the one who set the key (sometimes done by storing a unique value in the key, then checking it before deleting).</p>\n</li>\n<li>\n<p><strong>Handling Network Partitions.</strong> In a simple single-node Redis, a partition can lead to confusion if the lock holder is cut off but still thinks it holds the lock. On the other hand, Redis might expire the key after the TTL, and another client might acquire the lock. To address these edge cases in larger clusters, you can use the <a href=\"https://redis.io/docs/latest/develop/use/patterns/distributed-locks/\">Redlock algorithm</a>, which coordinates across multiple Redis instances to reduce the chance of conflicting lock ownership during network splits.</p>\n</li>\n</ol>\n<p>This approach suffices for a <strong>“best effort”</strong> distributed lock in most daily scenarios. However, if your environment is prone to frequent network partitions or requires extremely strict guarantees, more advanced configurations or tools might be necessary.</p>\n<p>Choosing Redis may be a good option if you already run a Redis cluster for caching or need a lightweight solution that’s easy to integrate.</p>\n<p>Performance is generally good, though absolute consistency under partitions can require more complex setups.</p>\n<hr>\n<h3 id=\"32-zookeeper--etcd-strongly-consistent-key-value-stores-with-ephemeral-nodes\" style=\"position:relative;\"><a href=\"#32-zookeeper--etcd-strongly-consistent-key-value-stores-with-ephemeral-nodes\" aria-label=\"32 zookeeper  etcd strongly consistent key value stores with ephemeral nodes permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>3.2 ZooKeeper / etcd (Strongly Consistent Key-Value Stores with Ephemeral Nodes)</h3>\n<p><strong>ZooKeeper and etcd each run as a cluster of nodes that keep data consistent across a majority of them (a quorum).</strong> When you write a small record saying, “I own this lock,” that record is replicated so every node in the cluster knows about it. This consistency helps avoid confusion if one node is temporarily out of sync.</p>\n<p><span\n      class=\"gatsby-resp-image-wrapper\"\n      style=\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 480px; height: auto\"\n    >\n      <a\n    class=\"gatsby-resp-image-link\"\n    href=\"/static/77f3eec921ac14de2536ea05c16d5a49/a5e48/image-3.jpg\"\n    style=\"display: block\"\n    target=\"_blank\"\n    rel=\"noopener\"\n  >\n    <span\n    class=\"gatsby-resp-image-background-image\"\n    style=\"padding-bottom: 75%; position: relative; bottom: 0; left: 0; background-image: url('data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAQBA//EABUBAQEAAAAAAAAAAAAAAAAAAAEA/9oADAMBAAIQAxAAAAGjZ0dUof/EABkQAAIDAQAAAAAAAAAAAAAAAAACAwQUAf/aAAgBAQABBQLFAYIRKcDG+M7fQS8qc//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABwQAAICAgMAAAAAAAAAAAAAAAABAiEQIhFRcf/aAAgBAQAGPwLm8P3sesrKjIejP//EABsQAQEBAAIDAAAAAAAAAAAAAAERACFhcYHx/9oACAEBAAE/IbCOHvIFaHlwXSMbSeV2rv7hgSt53//aAAwDAQACAAMAAAAQRO//xAAVEQEBAAAAAAAAAAAAAAAAAAAQEf/aAAgBAwEBPxCn/8QAFhEBAQEAAAAAAAAAAAAAAAAAAAER/9oACAECAQE/EE1//8QAHxABAQACAQQDAAAAAAAAAAAAAREAITFBUWGhkbHR/9oACAEBAAE/EEkweBU9428gf0xK71j3OvONTRJE0/OOGlBIRifTLBPai1l95//Z'); background-size: cover; display: block;\"\n  ></span>\n  <picture>\n          <source\n              srcset=\"/static/77f3eec921ac14de2536ea05c16d5a49/507e6/image-3.webp 200w,\n/static/77f3eec921ac14de2536ea05c16d5a49/28a80/image-3.webp 400w,\n/static/77f3eec921ac14de2536ea05c16d5a49/cdb16/image-3.webp 480w\"\n              sizes=\"(max-width: 480px) 100vw, 480px\"\n              type=\"image/webp\"\n            />\n          <source\n            srcset=\"/static/77f3eec921ac14de2536ea05c16d5a49/37402/image-3.jpg 200w,\n/static/77f3eec921ac14de2536ea05c16d5a49/4cda9/image-3.jpg 400w,\n/static/77f3eec921ac14de2536ea05c16d5a49/a5e48/image-3.jpg 480w\"\n            sizes=\"(max-width: 480px) 100vw, 480px\"\n            type=\"image/jpeg\"\n          />\n          <img\n            class=\"gatsby-resp-image-image\"\n            src=\"/static/77f3eec921ac14de2536ea05c16d5a49/a5e48/image-3.jpg\"\n            alt=\"Distributed Lock using Zookeeper\"\n            title=\"Distributed Lock using Zookeeper\"\n            loading=\"lazy\"\n            decoding=\"async\"\n            style=\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\"\n          />\n        </picture>\n  </a>\n    </span></p>\n<p>source: <a href=\"https://youtube.com/watch?v=a-zatYN1Lx0\">https://youtube.com/watch?v=a-zatYN1Lx0</a></p>\n<p><strong>You create something like</strong> <code class=\"language-text\">/locks/lockKey</code> <strong>in ZooKeeper or a key in etcd.</strong> If there is no record, you can successfully claim the lock. If it already exists, you either wait or fail, depending on your locking recipe.</p>\n<h4 id=\"releasing-lock\" style=\"position:relative;\"><a href=\"#releasing-lock\" aria-label=\"releasing lock permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Releasing Lock</h4>\n<p><strong>When you’re done, you delete the ephemeral node or lease key, signalling that source is available.</strong> Since it’s ephemeral, the node is removed if you just disconnect cleanly. But if you do explicit cleanup, that’s more predictable.</p>\n<p>Other clients can “watch” that lock path. If the lock holder crashes, ZooKeeper or etcd detects the session loss and removes the node, instantly notifying watchers that the lock is free again. This allows any waiting client to move in and grab the lock right away.</p>\n<p>If a node is isolated from the quorum, ZooKeeper or etcd eventually considers that session dead and removes the ephemeral node. This auto-frees the lock. The newly updated cluster state reflects that the lock is available, so another node can pick it up.</p>\n<h4 id=\"why-would-you-choose-zookeeper-or-etcd\" style=\"position:relative;\"><a href=\"#why-would-you-choose-zookeeper-or-etcd\" aria-label=\"why would you choose zookeeper or etcd permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Why would you Choose ZooKeeper or etcd?</h4>\n<p>If your environment already depends on them for cluster metadata or leader election, reusing them for locks is natural. They replicate data across multiple servers, so updates to the lock state are consistent, reducing the risk of split-brain scenarios. The ephemeral mechanism automatically frees locks if a session dies, so you don’t end up with “zombie” locks after crashes.</p>\n<p><strong>They offer stronger consistency guarantees at the cost of heavier operational overhead compared to Redis.</strong> Setting up a ZooKeeper or etcd cluster is more complex than a single Redis node. They also come with higher operational overhead, so they’re often chosen by teams already using these tools for other cluster metadata.</p>\n<hr>\n<h3 id=\"33-database-advisory-locks-using-your-existing-relational-db\" style=\"position:relative;\"><a href=\"#33-database-advisory-locks-using-your-existing-relational-db\" aria-label=\"33 database advisory locks using your existing relational db permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>3.3 Database Advisory Locks (Using Your Existing Relational DB)</h3>\n<p>Sometimes you’d rather avoid setting up additional infrastructure, like Redis or ZooKeeper, and simply rely on the single relational database you already use. Many SQL databases—PostgreSQL, MySQL, SQL Server, and others—provide built-in locking features that can help you coordinate concurrency directly in your existing environment. There are generally two ways to handle locks in a relational database:</p>\n<ul>\n<li>\n<p>row-level locks (like <code class=\"language-text\">SELECT ... FOR UPDATE</code>)</p>\n</li>\n<li>\n<p>advisory locks (like <code class=\"language-text\">pg_advisory_lock</code> or <code class=\"language-text\">GET_LOCK</code>)</p>\n</li>\n</ul>\n<p>They serve different needs, but both let you say, <em>“I want exclusive access to something”</em> using your existing database.</p>\n<p>Below is <strong>Section 3.3</strong> in a style <strong>matching</strong> the Redis or ZooKeeper/etcd examples. It keeps the <strong>same structure</strong>, including a <strong>“big picture”</strong> intro, a <strong>step-by-step</strong> explanation of how locks work, and a <strong>conclusion</strong> about <strong>when</strong> you’d choose this approach.</p>\n<hr>\n<h3 id=\"33-database-locks-monolithic-or-single-db-setup\" style=\"position:relative;\"><a href=\"#33-database-locks-monolithic-or-single-db-setup\" aria-label=\"33 database locks monolithic or single db setup permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>3.3 Database Locks (Monolithic or Single-DB Setup)</h3>\n<p>Sometimes, you’d rather avoid setting up extra infrastructure like Redis or ZooKeeper. If your entire application relies on a single relational database (e.g., PostgreSQL, MySQL, SQL Server), you can often leverage <strong>built-in locking</strong> features to manage concurrency. Many databases provide <strong>two</strong> main methods:</p>\n<ol>\n<li>\n<p><strong>Row-Level Locks</strong> (e.g., <code class=\"language-text\">SELECT ... FOR UPDATE</code>)</p>\n</li>\n<li>\n<p><strong>Advisory Locks</strong> (e.g., <code class=\"language-text\">pg_advisory_lock</code> in Postgres, <code class=\"language-text\">GET_LOCK</code> in MySQL, <code class=\"language-text\">sp_getapplock</code> in SQL Server)</p>\n</li>\n</ol>\n<p>They serve different needs, but both let you say, “I want exclusive access to something,” using your existing DB.</p>\n<h4 id=\"row-level-locks-with-select--for-update\" style=\"position:relative;\"><a href=\"#row-level-locks-with-select--for-update\" aria-label=\"row level locks with select  for update permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Row-Level Locks with <code class=\"language-text\">SELECT ... FOR UPDATE</code></h4>\n<p>You can lock specific rows in a table by issuing something like:</p>\n<div class=\"gatsby-highlight\" data-language=\"sql\"><pre class=\"language-sql\"><code class=\"language-sql\"><span class=\"token keyword\">BEGIN</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">SELECT</span> <span class=\"token operator\">*</span> <span class=\"token keyword\">FROM</span> locks <span class=\"token keyword\">WHERE</span> lock_id <span class=\"token operator\">=</span> <span class=\"token variable\">@loc_key</span> <span class=\"token keyword\">FOR</span> <span class=\"token keyword\">UPDATE</span><span class=\"token punctuation\">;</span>\n<span class=\"token comment\">/* make changes */</span>\n<span class=\"token keyword\">COMMIT</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>That looks cryptic, but here’s what’s happening.</p>\n<p>You wrap the SQL command with the transaction (BEGIN/COMMIT). The query includes <code class=\"language-text\">FOR UPDATE</code>, which tells the database: <em>“Lock this row so nobody else can modify it until I’m done.”</em> If someone else tries to update the same row, they’ll have to wait.</p>\n<p><strong>The lock lasts until you commit or roll back the transaction.</strong> At commit, the lock is released, letting other transactions proceed.</p>\n<p>You may also use regular rows instead of a dedicated locks table. It’s natural if your concurrency problem revolves around specific table rows. For others you need to define key that would represent the scope of locking</p>\n<h4 id=\"advisory-locks-eg-pg_advisory_lock-or-get_lock\" style=\"position:relative;\"><a href=\"#advisory-locks-eg-pg_advisory_lock-or-get_lock\" aria-label=\"advisory locks eg pg_advisory_lock or get_lock permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Advisory Locks (e.g., <code class=\"language-text\">pg_advisory_lock</code> or <code class=\"language-text\">GET_LOCK</code>)</h4>\n<p>Instead of tying a lock to a specific row, you can lock an <strong>arbitrary</strong> identifier:</p>\n<div class=\"gatsby-highlight\" data-language=\"sql\"><pre class=\"language-sql\"><code class=\"language-sql\"><span class=\"token comment\">-- PostgreSQL</span>\n<span class=\"token keyword\">SELECT</span> pg_advisory_lock<span class=\"token punctuation\">(</span><span class=\"token number\">12345</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token comment\">/* do something exclusive */</span>\n<span class=\"token keyword\">SELECT</span> pg_advisory_unlock<span class=\"token punctuation\">(</span><span class=\"token number\">12345</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<div class=\"gatsby-highlight\" data-language=\"sql\"><pre class=\"language-sql\"><code class=\"language-sql\"><span class=\"token comment\">-- MySQL</span>\n<span class=\"token keyword\">SELECT</span> GET_LOCK<span class=\"token punctuation\">(</span><span class=\"token string\">'readModel'</span><span class=\"token punctuation\">,</span> <span class=\"token number\">10</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token comment\">/* do something exclusive */</span>\n<span class=\"token keyword\">SELECT</span> RELEASE_LOCK<span class=\"token punctuation\">(</span><span class=\"token string\">'readModel'</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span></code></pre></div>\n<p>You pass an integer (<code class=\"language-text\">12345</code>) or string (<code class=\"language-text\">'readModel'</code>) to request a lock from the database’s internal lock manager. If it’s “free”, you acquire it; if it’s already taken, you wait or fail (depending on the timeout).</p>\n<p><strong>Locks are session-bound.</strong> The lock belongs to your current DB session. The lock is freed automatically if that session ends due to a crash or normal exit. The downside is that the lock might remain locked if your session hangs around in a weird state.</p>\n<p>Those locks can be much lighter than row-level locks (e.g. <a href=\"https://www.postgresql.org/docs/current/explicit-locking.html#ADVISORY-LOCKS\">PostgreSQL Advisory Locks</a>). Yet, some of them may require passing integers, so you may need to map the string to a number, which can require some consistent hashing algorithm.</p>\n<p><strong>If you want the reference implementations of PostgreSQL Advisory Locks, <a href=\"https://github.com/event-driven-io/Pongo/blob/main/src/packages/dumbo/src/postgres/core/locks/advisoryLocks.ts\">check mine in Pongo</a>.</strong></p>\n<h4 id=\"why-would-you-use-database-locks\" style=\"position:relative;\"><a href=\"#why-would-you-use-database-locks\" aria-label=\"why would you use database locks permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Why would you use database locks?</h4>\n<p>In many cases, database locks require minimal setup. There’s no need to spin up Redis or ZooKeeper if you already trust a single relational DB for everything. You can use familiar SQL, which can benefit many developers. You get transaction integrations.</p>\n<p><strong>Still, they’re tight to a single database scope.</strong> They don’t help if you have multiple databases or multi-region replication.</p>\n<p><strong>The other downside is lock contention.</strong> A busy system constantly grabbing locks could block queries and reduce performance.</p>\n<p><strong>You also can get deadlocks</strong> if multiple transactions lock rows or advisory locks in different orders. Monitoring is crucial.</p>\n<hr>\n<h3 id=\"34-kubernetes-single-instance-deployments-an-operational-constraint\" style=\"position:relative;\"><a href=\"#34-kubernetes-single-instance-deployments-an-operational-constraint\" aria-label=\"34 kubernetes single instance deployments an operational constraint permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>3.4 Kubernetes Single-Instance Deployments (An Operational Constraint)</h3>\n<p>Sometimes, you don’t need a distributed lock at all—you just need to ensure there’s no possibility of concurrency. In Kubernetes, you can tell the cluster to run exactly one replica of your service. With only one pod, you don’t risk two pods writing to the same resource simultaneously. This approach is straightforward but also very limiting.</p>\n<h4 id=\"how-it-works-in-terms-of-locking\" style=\"position:relative;\"><a href=\"#how-it-works-in-terms-of-locking\" aria-label=\"how it works in terms of locking permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a><strong>How It Works in Terms of Locking</strong></h4>\n<ul>\n<li>\n<p><strong>No Actual Lock</strong>: Technically, you don’t lock anything. You simply rely on the fact that setting <code class=\"language-text\">replicas: 1</code> in your Deployment or <a href=\"https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/\">StatefulSet</a> ensures the scheduler never spawns more instances. You specify exactly one replica for your service. Kubernetes never spawns a second one, so concurrency can’t occur at the application level.</p>\n</li>\n<li>\n<p><strong>One Pod, One Writer</strong>: If that single pod is alive, it’s the only one doing the work. If it crashes, Kubernetes restarts it, but you might have downtime.</p>\n</li>\n<li>\n<p><strong>No Concurrency</strong>: Because there’s literally just one instance, you don’t need to coordinate or lock anything. You’re effectively sidestepping the whole locking problem</p>\n</li>\n<li>\n<p><strong>No Parallelism or scaling</strong>: If you eventually need multiple pods, you’ll have to switch to an actual lock approach. If traffic spikes or you want high availability, you can’t spin up more pods without moving to a distributed locking mechanism.</p>\n</li>\n</ul>\n<p><strong>Funnily enough, Kubernetes stores all cluster data in <a href=\"https://etcd.io/\">etcd</a>.</strong> <a href=\"https://etcd.io/\">etcd</a> keeps the entire cluster’s state: Deployments, Services, ConfigMaps, and so on. However, the cluster doesn’t perform ephemeral lock logic for a single-instance Deployment. It merely stores “replicas: 1” and lets the controller keep the Pod count in sync with that number.</p>\n<p>Kubernetes doesn’t use the <em>lock record</em> as described before with Zookeeper/etcd section. The Deployment/Stateful Set controller periodically checks how many Pods are currently running for that Deployment. If there are zero (e.g., the Pod crashed), the controller starts a new Pod to reach the desired count of one. If there’s already one Pod, the controller does nothing—it matches the desired state.</p>\n<p><strong>In Kubernetes, the mechanisms and controllers designed to manage the number of pods are quite robust, but in highly dynamic or unusual situations, there might be brief moments where conditions could lead to more than one pod being created temporarily.</strong> However, such occurrences are generally rare and usually quickly corrected by Kubernetes’ control loops. For instance:</p>\n<ul>\n<li>\n<p><strong>Rapid Scale-Up and Scale-Down:</strong> If you rapidly change the replicas count up and then back down (or vice versa), it might lead to the Kubernetes scheduler and the replica set controller temporarily creating more pods than desired. This can occur in highly dynamic environments where scripts or operators frequently adjust the replica counts.</p>\n</li>\n<li>\n<p><strong>Controller Delays:</strong> Kubernetes controllers periodically reconcile the desired state with the actual state. If there’s a delay or lag in the controller’s loop (due to cluster load or other disruptions), you might briefly see more pods than expected.</p>\n</li>\n<li>\n<p><strong>Network Partitions or etcd Availability Issues:</strong> If parts of the cluster cannot communicate with each other or with etcd (where the desired state is stored), different nodes might make different decisions. For example, one node might not know that a new pod has already been scheduled elsewhere and might schedule another.</p>\n</li>\n</ul>\n<p><strong>Why You’d Choose Single-Instance in Kubernetes?</strong></p>\n<p>If you just need to enforce that there’s a single instance processing background job (read model handling, job processing) and you have Kubernetes set up, then it’s a decent choice. You remove the concurrency at all. You don’t spin up Redis or ZooKeeper just to handle locks.</p>\n<p><strong>Still, the race conditions can be dangerous for high traffic or important cases. It’s not fully reliable.</strong> So, it’s a decent choice for <strong>rarely Triggered Tasks</strong>: If you have a once-a-day job and don’t care about parallelism or availability, one pod might be enough.</p>\n<hr>\n<h3 id=\"35-tldr-on-locking-tooling-choice\" style=\"position:relative;\"><a href=\"#35-tldr-on-locking-tooling-choice\" aria-label=\"35 tldr on locking tooling choice permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>3.5 TLDR on locking tooling choice</h3>\n<p>Here’s my recommended decision-making scheme for locking mechanism:</p>\n<ol>\n<li>\n<p><strong>If you have a single relational DB handling all app state, advisory locks might suffice</strong> (and require no extra moving parts and minimal infrastructure). Many ready-made packages provide that. Inside modules locks or monolithic apps using a single DB instance are good use cases here. Beware: database locks are harder to scale across multiple DB nodes. They can become a contention point if locks are frequent.</p>\n</li>\n<li>\n<p><strong>If your environment already includes a Redis cluster for caching and you want a simpler ephemeral lock, Redis is a natural fit.</strong> If you want an application-level lock across multiple services, Redis is a decent candidate. Remember that it can bring a potential edge case under network partitions; multi-node setups may require Redlock for stronger safety.</p>\n</li>\n<li>\n<p><strong>If concurrency is never desired or is completely out of scope for a specific microservice, AND you’re using already Kubernetes a single-instance Kubernetes approach may be acceptable.</strong> However, it clearly sacrifices high availability and scaling. It’s not really a distributed lock at all, just an operational limit of “one pod.”</p>\n</li>\n<li>\n<p><strong>If your microservices need advanced coordination (like leader election, watchers, or strongly consistent state), consider ZooKeeper or etcd.</strong> It’s the most configurable option and the most scalable. Yet it’s heavier to operate than a simple single Redis or DB solution. It’s also another service to operate. And pretty complex one.</p>\n</li>\n</ol>\n<hr>\n<h2 id=\"4-potential-pitfalls-and-best-practices\" style=\"position:relative;\"><a href=\"#4-potential-pitfalls-and-best-practices\" aria-label=\"4 potential pitfalls and best practices permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>4. Potential Pitfalls and Best Practices</h2>\n<h3 id=\"41-deadlocks-and-lock-ordering\" style=\"position:relative;\"><a href=\"#41-deadlocks-and-lock-ordering\" aria-label=\"41 deadlocks and lock ordering permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>4.1 Deadlocks and Lock Ordering</h3>\n<p>If your system needs to acquire multiple locks at once, you risk deadlocks (e.g., process A has Lock1 and wants Lock2; process B has Lock2 and wants Lock1). The best practice is to lock in a consistent order globally or use carefully designed transaction boundaries. I’ll show you next week how you could use queuing and single writer for that.</p>\n<h3 id=\"42-lock-contention\" style=\"position:relative;\"><a href=\"#42-lock-contention\" aria-label=\"42 lock contention permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>4.2 Lock Contention</h3>\n<p>Locks serialize access. If too many services fight for the same lock, your system effectively becomes single-threaded. To avoid bottlenecks, lock only the smallest critical sections. If concurrency at some granularity is acceptable, consider sharding or partitioned locks.</p>\n<h3 id=\"43-crash-handling-ttl-ephemeral-sessions\" style=\"position:relative;\"><a href=\"#43-crash-handling-ttl-ephemeral-sessions\" aria-label=\"43 crash handling ttl ephemeral sessions permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>4.3 Crash Handling (TTL, Ephemeral Sessions)</h3>\n<p>What if the lock holder dies mid-operation? This is the classic reason for <strong>TTL</strong> or ephemeral sessions:</p>\n<ul>\n<li>\n<p><strong>TTL</strong>: If the process doesn’t extend the TTL, the lock is auto-freed. Another node can proceed.</p>\n</li>\n<li>\n<p><strong>Ephemeral</strong>: The ephemeral node is removed if the session disappears (like in ZooKeeper).</p>\n</li>\n</ul>\n<p>Without these, you risk <strong>permanent blocking</strong> if someone forgets to release or if the holder abruptly goes offline.</p>\n<h3 id=\"44-single-point-of-failure\" style=\"position:relative;\"><a href=\"#44-single-point-of-failure\" aria-label=\"44 single point of failure permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>4.4 Single Point of Failure</h3>\n<p>If you rely on a single Redis instance or a single ZooKeeper node, your lock manager can fail. Always consider using a clustered or highly available setup, such as Redis with sentinel or cluster mode or a ZooKeeper ensemble of three or five nodes.</p>\n<h3 id=\"45-clock-skew-and-network-partitions\" style=\"position:relative;\"><a href=\"#45-clock-skew-and-network-partitions\" aria-label=\"45 clock skew and network partitions permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>4.5 Clock Skew and Network Partitions</h3>\n<p>Systems like Redlock try to handle partial failures, but no distributed lock can be 100% guaranteed if your network is severely partitioned (CAP theorem territory). You might end up with multiple holders, each believing they’re the only one. Proper design, timeouts, and conflict detection help reduce these edge cases.</p>\n<hr>\n<h2 id=\"conclusion\" style=\"position:relative;\"><a href=\"#conclusion\" aria-label=\"conclusion permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Conclusion</h2>\n<p>Distributed locks are a fundamental tool for <strong>coordinating concurrency</strong> across systems. Whether you choose Redis (with TTL-based locks), ZooKeeper (ephemeral nodes), database locks, or even just run a single replica in Kubernetes, the core principle remains: only one party can operate on a resource at a time. Handling <strong>failures</strong> (crashed holders) is crucial, which is why ephemeral or time-based mechanisms are standard. Each approach has trade-offs in complexity, performance, and fault-tolerance.</p>\n<p><strong>Key Takeaways</strong> for senior engineers and architects:</p>\n<ol>\n<li>\n<p><strong>Try to avoid distributed locking if you can.</strong> Usually, they’re a good sanity checkpoint to validate why you got here and if you really need it.</p>\n</li>\n<li>\n<p><strong>Use locks carefully</strong>: Over-locking can degrade concurrency, but it’s critical when data corruption or double-processing must be prevented.</p>\n</li>\n<li>\n<p><strong>Choose the right store</strong>: Redis is often the simplest if you already run it. ZooKeeper suits advanced coordination. DB locks can be fine for single-region or monolithic architectures.</p>\n</li>\n<li>\n<p><strong>Plan for failure</strong>: Implement TTL/ephemeral locks to avoid “zombie” or stuck locks.</p>\n</li>\n<li>\n<p><strong>Zero concurrency?</strong>: Setting <code class=\"language-text\">replicas: 1</code> in a StatefulSet or Deployment, concurrency is removed but multi-replica scaling is forfeited. It’s not a distributed lock—just an operational constraint.</p>\n</li>\n<li>\n<p><strong>Understand trade-offs</strong>: If your scenario is straightforward, a lock-based single subscription might be simpler than full leader election. For advanced cluster coordination, leader election or a robust distributed approach might be more robust.</p>\n</li>\n</ol>\n<p>Distributed systems will always have complexities, but a well-implemented distributed lock (or a strategic single-instance approach) can tame the chaos of concurrency—keeping your data consistent and your architecture stable.</p>\n<p><strong>What are your experiences, use cases, and challenges with distributed locking?</strong></p>\n<p>And hey, all the best for Christmas if you celebrate it. If you don’t, try to also get the chance to rest a bit. And if you don’t want to, that’s fine, as long as you’re happy!</p>\n<p>Cheers!</p>\n<p>Oskar</p>\n<p><strong>p.s. Ukraine is still under brutal Russian invasion. A lot of Ukrainian people are hurt, without shelter and need help.</strong> You can help in various ways, for instance, directly helping refugees, spreading awareness, and putting pressure on your local government or companies. You can also support Ukraine by donating, e.g. to the <a href=\"https://savelife.in.ua/en/donate/\">Ukraine humanitarian</a> organisation, <a href=\"https://www.gofundme.com/f/help-to-save-the-lives-of-civilians-in-a-war-zone\">Ambulances for Ukraine</a> or <a href=\"https://redcross.org.ua/en/\">Red Cross</a>.</p>","excerpt":"Was your data ever mysteriously overwritten? No? Think again. Have you noticed conflicting updates to the same data? Still nope? Lucky you! I had cases when my data…","fields":{"slug":"/distributed-locking-a-practical-guide/","originalSlug":"/distributed-locking-a-practical-guide/","prefix":"2024-12-23","langKey":"pl","source":"posts"},"frontmatter":{"title":"Distributed Locking: A Practical Guide","description":null,"summary":null,"author":"oskar dudycz","category":"Software Architecture","categories":null,"publishedAt":null,"disqusId":null,"useDefaultLangCanonical":true,"cover":{"childImageSharp":{"resize":{"src":"/static/fb28a3149c35ba02ce9102df4a9c61c4/2a4de/2024-12-23-cover.png"}}}}},"authornote":{"id":"88a0544e-7b8b-5684-bdb0-5456ac60471c","html":"<p><strong>Oskar Dudycz</strong> jest niezależnym architektem oprogramowania i konsultantem, który pomaga zespołom projektować i dostarczać Systemy Oparte na zdarzeniach. Rozwija projekty Open Source takie jak <a href=\"https://event-driven-io.github.io/emmett/\">Emmett</a> i dzieli się praktycznym doświadczeniem z Event Sourcingiem, CQRS-em oraz architekturą oprogramowania. Dowiedz się więcej <a href=\"/pl/about/\">o Oskarze</a>, poznaj jego <a href=\"/pl/training/\">szkolenia i warsztaty</a> albo <a href=\"/pl/consulting/\">porozmawiaj o konsultingu</a>.</p>"},"site":{"siteMetadata":{"facebook":{"appId":""}}}},"pageContext":{"slug":"/distributed-locking-a-practical-guide/","lang":"pl","langKey":"pl","prev":{"id":"722180ec-c77d-5ba9-815a-5d198c23bbd0","fields":{"slug":"/on-getting-the-meaningful-discussions/","prefix":"2024-12-16","source":"posts","langKey":"pl"},"frontmatter":{"title":"On getting the meaningful discussions, and why that's important","category":"Software Architecture","categories":null,"related":null,"redirectFrom":null,"redirectAliases":null,"useDefaultLangCanonical":true}},"next":{"id":"c2230875-ce85-55a1-a8c7-92406e3c0572","fields":{"slug":"/locks-queues-and-business-workflows/","prefix":"2024-12-30","source":"posts","langKey":"pl"},"frontmatter":{"title":"Locks, Queues and business workflows processing","category":"Software Architecture","categories":null,"related":null,"redirectFrom":null,"redirectAliases":null,"useDefaultLangCanonical":true}},"source":"posts","originalPath":"/distributed-locking-a-practical-guide/","availableLanguages":["en","pl"],"canonicalLanguages":["en"],"relatedIds":[],"excludeFromSitemap":true,"language":"pl","i18n":{"language":"pl","languages":["en","pl"],"defaultLanguage":"en","generateDefaultLanguagePage":true,"routed":true,"originalPath":"/distributed-locking-a-practical-guide/","path":"/pl/distributed-locking-a-practical-guide/"}}},"staticQueryHashes":["3287778089"],"slicesMap":{}}