<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Hancic</title>
    <description>The latest articles on DEV Community by Hancic (@hancic128).</description>
    <link>https://dev.to/hancic128</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4120616%2Feaadb346-09c8-4beb-945a-e7413ef70bda.png</url>
      <title>DEV Community: Hancic</title>
      <link>https://dev.to/hancic128</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hancic128"/>
    <language>en</language>
    <item>
      <title>Cache Invalidation: Three Patterns and the Cost of Getting It Wrong</title>
      <dc:creator>Hancic</dc:creator>
      <pubDate>Fri, 18 Sep 2026 13:42:16 +0000</pubDate>
      <link>https://dev.to/hancic128/cache-invalidation-three-patterns-and-the-cost-of-getting-it-wrong-2kc0</link>
      <guid>https://dev.to/hancic128/cache-invalidation-three-patterns-and-the-cost-of-getting-it-wrong-2kc0</guid>
      <description>&lt;p&gt;A version-control service I worked on kept a small list in Redis: which version of the code bundle was sitting on the shared NFS volume. Every request checked the list first. Hit meant the file was local and the executor could load it. Miss meant a download from object storage.&lt;/p&gt;

&lt;p&gt;One morning the cleanup job ran. It deletes old versions when NFS fills up. It deleted v1.42 from NFS. The list still said v1.42 existed. The cache lied, and a lying cache is worse than a missing one. A miss sends you to the source of truth. A false hit tells you the file is there, the loader opens it, and you get an error three steps later with no clear culprit.&lt;/p&gt;

&lt;p&gt;Picking Redis or Caffeine or Memcached was not the question. The question was how to keep that list aligned with NFS. Every cache, on every system, faces the same question. Three patterns answer it, and each one hands you a different bill.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbu1v1jtlayoksyl8a87t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbu1v1jtlayoksyl8a87t.png" alt="Three patterns, three bills" width="800" height="276"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Three patterns, three bills&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  TTL: the default that lies until it stops
&lt;/h2&gt;

&lt;p&gt;Most teams pick TTL because it costs nothing to set up. Each key gets an expiration time. Read the key. If the time has passed, treat it as a miss and reload from the source. Done.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftegjxijfxw0kp0ujhdpx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftegjxijfxw0kp0ujhdpx.png" alt="TTL cache flow from write to read to expiry" width="800" height="267"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;TTL cache flow from write to read to expiry&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The good part is the simplicity. The bad part is the gap between expiration and the actual re-read. Anything that writes to the source before that re-read lands will be missed. Every miss costs a trip to the source, which is fine until the source is slow, and then the cache that was supposed to take load starts generating it.&lt;/p&gt;

&lt;p&gt;Tunables: expiration time, refresh-ahead. Neither removes the window. Shorter expiration shrinks it. Longer expiration makes the cache useful again. Pick one bill and own it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Event-driven: the cache stays honest, the event bus stays busy
&lt;/h2&gt;

&lt;p&gt;The second pattern answers the lying problem directly. Instead of letting entries time out, you tell the cache when the source changed. Every write to the source emits an event. A consumer in the cache layer picks it up and invalidates or updates the key.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4094a7yim22cw0w5mi7i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4094a7yim22cw0w5mi7i.png" alt="Event-driven invalidation races against writes" width="800" height="267"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Event-driven invalidation races against writes&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Two failure modes show up in practice.&lt;/p&gt;

&lt;p&gt;First, missed events. The producer crashed, the network dropped the message, the consumer was down. The cache says the old value is still right. The window is smaller than TTL because it depends on the outage, but the window is never zero.&lt;/p&gt;

&lt;p&gt;Second, out-of-order events. Two writes happened in quick succession. The first invalidation lands, then the second one lands before the first write has been read back into the cache. A reader who arrives between them sees nothing, reloads the first write, then the second invalidation hits and evicts it. Net effect: a clean cache, one wasted reload. If the order reverses, the cache holds the older value while the source has the newer one. That is the same lying cache, just for shorter.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F98sw3t17epobbsgjcfem.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F98sw3t17epobbsgjcfem.png" alt="Messages can be lost" width="799" height="244"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Messages can be lost&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The bill is the event bus. A queue with delivery guarantees, idempotent consumers, retries with backoff, dead-letter handling, ordering where it matters. Each of these is real engineering, and the system that owns the source has to wire it in. Event-driven caches are not free. They are cheap to write the first time and expensive to operate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Write-through: the cache never holds a lie
&lt;/h2&gt;

&lt;p&gt;The third pattern removes the lying window by removing the path that creates it. The cache sits on the write path. Every write goes to the cache and the source. Reads go to the cache. There is no separate invalidation step because there is no separate write.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsl3g33kqwhtdizvmyeu0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsl3g33kqwhtdizvmyeu0.png" alt="Write-through keeps cache and source in step" width="800" height="267"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Write-through keeps cache and source in step&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The tradeoff moves up the stack. Writes get slower because they pay two round-trips, and the cache becomes part of the critical path. If the cache layer fails, writes fail. The source has to be the system of record that recovers state on restart.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fugyee2wcx0m52u5gm8mo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fugyee2wcx0m52u5gm8mo.png" alt="Pick one, or pay three bills" width="800" height="311"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Pick one, or pay three bills&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This works well when reads dominate writes by a wide margin and the cache layer can match the source's durability story. It works badly when the source and the cache disagree on failure modes, because now you have two systems that both believe they own the truth.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pick one and own its failure mode
&lt;/h2&gt;

&lt;p&gt;The three patterns answer the same question with three different bills.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TTL gives you a cache that lies until the timer expires. You own the gap.&lt;/li&gt;
&lt;li&gt;Event-driven gives you a cache that stays honest when the events flow. You own the event bus.&lt;/li&gt;
&lt;li&gt;Write-through gives you a cache that never holds a lie. You own the write path.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is no pattern that has none of these bills. The list of caches in your system is the list of failure modes you have agreed to operate.&lt;/p&gt;

&lt;p&gt;When the lying cache shows up in an incident report, look at which pattern the cache is using. The failure mode is not a surprise. It is the bill.&lt;/p&gt;

</description>
      <category>caching</category>
      <category>distributedsystems</category>
      <category>backend</category>
      <category>consistency</category>
    </item>
    <item>
      <title>Where Distributed Systems Complexity Comes From</title>
      <dc:creator>Hancic</dc:creator>
      <pubDate>Fri, 11 Sep 2026 13:54:52 +0000</pubDate>
      <link>https://dev.to/hancic128/where-distributed-systems-complexity-comes-from-3nop</link>
      <guid>https://dev.to/hancic128/where-distributed-systems-complexity-comes-from-3nop</guid>
      <description>&lt;p&gt;Three machines in a rack are still three machines. They become a distributed system when they have to finish one job together, and that requirement is where the trouble starts.&lt;/p&gt;

&lt;p&gt;On a single machine you get three things for free. Every thread shares the same memory, so a write lands and everyone sees it. One clock puts every event in order. One lock protects the shared state.&lt;/p&gt;

&lt;p&gt;Move that job across a network and all three disappear.&lt;/p&gt;

&lt;p&gt;Everything hard about distributed systems comes from three root problems: space, time, and consensus. Space shows up first and you notice it. Time is quieter, and you misread it. Consensus is what you get once the first two pile up.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5la6f8i6ydx5and8nbst.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5la6f8i6ydx5and8nbst.png" alt="Space and time become consensus" width="800" height="258"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Space and time become consensus&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Space: the data lives in more than one place
&lt;/h2&gt;

&lt;p&gt;Sharing a variable inside one process costs nothing. Run the same code on two machines and each node holds a piece of the truth. No node holds all of it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Replication: who gets to write
&lt;/h3&gt;

&lt;p&gt;Let every node write and two of them will accept conflicting writes for the same key. Neither has heard from the other yet, so both believe they are right. Now you need a rule for that conflict.&lt;/p&gt;

&lt;p&gt;Restrict writes to one leader and you inherit a different set of problems. Then the leader dies, and you own the seconds that follow. You also have to decide which reads may go to a follower that lags behind.&lt;/p&gt;

&lt;p&gt;Single-leader, multi-leader, and leaderless replication are three answers to the write question. Each one leaves you a different set of failures.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj46rnd0e8vkdkslwesov.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj46rnd0e8vkdkslwesov.png" alt="Who may write" width="799" height="333"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Who may write&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Partitioning: where data goes
&lt;/h3&gt;

&lt;p&gt;The dataset outgrows one disk. Split by key range and the ranges go out of balance as traffic shifts. Split by hash and each node gets a similar share of writes. Range scans get slower, and a query that needs rows from two partitions pays a network hop.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Forzpbli27z7xg73ng5wq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Forzpbli27z7xg73ng5wq.png" alt="Range versus hash" width="799" height="262"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Range versus hash&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The two problems multiply. One node can lead one partition and follow another, so the replication policy has to match the partition layout. The topology you draw on a whiteboard becomes a set of rules every node has to agree on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Time: order stops being obvious
&lt;/h2&gt;

&lt;p&gt;Code on one machine gives you a total order. The line &lt;code&gt;a = 1&lt;/code&gt; finishes before the line &lt;code&gt;b = a + 1&lt;/code&gt; reads it, because the hardware guarantees that.&lt;/p&gt;

&lt;p&gt;Across machines there's no global clock. Two machines drift apart and NTP holds them within milliseconds, which is slow when you handle thousands of operations per second. The network reorders messages on top of that. You send A, then B, and the receiver gets B first.&lt;/p&gt;

&lt;p&gt;Read a timestamp and you still can't say which event happened first.&lt;/p&gt;

&lt;h3&gt;
  
  
  Clocks that count events
&lt;/h3&gt;

&lt;p&gt;Drop wall-clock time and ask a smaller question instead. Compare two events: did one cause the other?&lt;/p&gt;

&lt;p&gt;If A causes B, then A is earlier. If neither causes the other, the two ran concurrently. A logical clock records that relation and gives you a partial order.&lt;/p&gt;

&lt;p&gt;One counter carries one number, so it can't separate concurrency from causation. Vector clocks fix that. Each node keeps one counter per node. Comparing two vectors tells you whether one event caused the other, or whether the two just happened in parallel.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftem94slp87y7015bnlpz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftem94slp87y7015bnlpz.png" alt="Causal, or concurrent" width="800" height="280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Causal, or concurrent&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The price grows with the cluster. A 500-node system attaches 500 counters to every message, and trimming old entries means accepting wrong answers.&lt;/p&gt;

&lt;h3&gt;
  
  
  The consistency spectrum
&lt;/h3&gt;

&lt;p&gt;Ordering uncertainty is why you choose from a spectrum of consistency models.&lt;/p&gt;

&lt;p&gt;Linearizability makes every operation look like it took effect at one instant on one machine. Sequential consistency drops the real-time requirement and keeps one global order. Causal consistency orders causes before effects and ignores the rest. Eventual consistency promises that replicas converge once writes stop, and stays quiet about when.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fatebnp0cnon9i2tmv6uc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fatebnp0cnon9i2tmv6uc.png" alt="Strong to weak" width="800" height="218"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Strong to weak&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Each step down that ladder buys you latency and availability, and costs you a rule you can reason about.&lt;/p&gt;

&lt;h2&gt;
  
  
  Consensus: agreement is the hard part
&lt;/h2&gt;

&lt;p&gt;Stack space on top of time and you get the problem distributed systems are famous for.&lt;/p&gt;

&lt;p&gt;You run into consensus in a lot of disguises. A cluster elects a leader. A transaction spans two databases and has to commit on both or neither. Two clients want the same row locked.&lt;/p&gt;

&lt;p&gt;Every one of those reduces to the same requirement. A group of nodes that don't trust each other, talking over a network that drops and delays messages, has to agree on one value.&lt;/p&gt;

&lt;h3&gt;
  
  
  The FLP result
&lt;/h3&gt;

&lt;p&gt;In an asynchronous system, if one node can crash, no deterministic protocol can guarantee consensus.&lt;/p&gt;

&lt;p&gt;The word that matters is deterministic. The theorem covers protocols that never give a wrong answer. Protocols that get it right with high probability sit outside it, and that gap is where Paxos and Raft live. Both use a quorum and randomized timeouts, so they finish in practice without ever being certain in theory.&lt;/p&gt;

&lt;h3&gt;
  
  
  Paxos, Raft, and ZAB
&lt;/h3&gt;

&lt;p&gt;Paxos is correct and hard to implement. Raft splits the same problem into leader election, log replication, and safety, which makes the protocol teachable. ZAB drives ZooKeeper and takes a similar route with ordering guarantees on top.&lt;/p&gt;

&lt;p&gt;All three need a majority to get anything done. A five-node cluster survives two failures. Split it 3 to 2 and the minority side can't serve writes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8pcqe84d0g161fvei7q2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8pcqe84d0g161fvei7q2.png" alt="Majority writes, minority waits" width="800" height="258"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Majority writes, minority waits&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Distributed transactions
&lt;/h3&gt;

&lt;p&gt;Two-phase commit asks every participant to prepare, then tells everyone to commit. That window between the two phases is the weak point. If the coordinator dies after the prepares, participants sit there holding locks.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmt4sw99v8236k3o4kecg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmt4sw99v8236k3o4kecg.png" alt="Prepare, vote, commit" width="800" height="311"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Prepare, vote, commit&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Three-phase commit adds a timeout step that shrinks the window without closing it. Saga and TCC commit each step and define a compensating action for it, which trades isolation for availability. One step fails and you spend the following week untangling a state that no single snapshot explains.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three problems stack
&lt;/h2&gt;

&lt;p&gt;Space, time, and consensus don't sit side by side. Space is the one you notice, because a node hands back stale data and the gap shows up in a request.&lt;/p&gt;

&lt;p&gt;Time is harder to catch. The bugs it causes look like space bugs until you check clock skew or measure how far behind a follower is.&lt;/p&gt;

&lt;p&gt;Consensus needs both before it bites. Agreement only gets hard once the data lives in several places and the messages arrive in an order you can't predict.&lt;/p&gt;

&lt;p&gt;That explains the size of the toolbox. Vector clocks, quorums, consistent hashing, leader election, compensation protocols. Different angles on one question: several machines have to finish one job, and each machine sees only its own local state.&lt;/p&gt;

&lt;p&gt;Next time a design review stalls on gossip versus Raft, or on two-phase commit versus Saga, name the problem you are paying for.&lt;/p&gt;

&lt;p&gt;With replication you decide who writes. With partitioning you decide where data lives. With time you decide whether ordering matters. With consensus you decide what the group accepts.&lt;/p&gt;

</description>
      <category>distributedsystems</category>
      <category>consensus</category>
      <category>replication</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
