<?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: Santosh Koti</title>
    <description>The latest articles on DEV Community by Santosh Koti (@santosh_koti).</description>
    <link>https://dev.to/santosh_koti</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3815422%2Fcc88c93d-db7f-4b18-ba5f-6f758fec34d1.png</url>
      <title>DEV Community: Santosh Koti</title>
      <link>https://dev.to/santosh_koti</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/santosh_koti"/>
    <language>en</language>
    <item>
      <title>Kubernetes Object Hierarchy: A Simple Mental Model</title>
      <dc:creator>Santosh Koti</dc:creator>
      <pubDate>Tue, 21 Apr 2026 05:00:54 +0000</pubDate>
      <link>https://dev.to/santosh_koti/kubernetes-object-hierarchy-a-simple-mental-model-min</link>
      <guid>https://dev.to/santosh_koti/kubernetes-object-hierarchy-a-simple-mental-model-min</guid>
      <description>&lt;p&gt;Kubernetes has a lot of objects. Don't think of them as a flat list. That's the wrong mental model. The right one is this: &lt;strong&gt;Kubernetes is a declarative control system.&lt;/strong&gt; You describe the state you want, and a set of objects — each with a narrow job — cooperate to make reality match. Once you see how those objects fit together, the rest of Kubernetes is mostly details.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; this post covers Kubernetes &lt;strong&gt;objects&lt;/strong&gt; — the resources you define in YAML. The &lt;strong&gt;control plane components&lt;/strong&gt; that make these objects work (API server, scheduler, controller manager, etcd) are a separate layer and not shown here.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Visual Hierarchy
&lt;/h2&gt;

&lt;p&gt;The first thing to get right is that Kubernetes partitions a cluster two different ways — physically (Nodes) and logically (Namespaces). These are &lt;strong&gt;orthogonal&lt;/strong&gt;, not nested. A Namespace spans many Nodes; a Node hosts Pods from many Namespaces. Pods are where the two views meet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[              Cluster              ]
       /                       \
[   Nodes   ]             [ Namespaces ]
 (physical)                  (logical)
       \                       /
        \                     /
         ▼                   ▼
         ┌─────────────────┐
         │      Pods       │  ← scheduled onto a Node, scoped to a Namespace
         └─────────────────┘

Inside each Namespace:

  [ Ingress ] ──► [ Service ] ──► [ Workload Controller ]
                                   (Deployment / StatefulSet /
                                    DaemonSet / Job)
                                        │
                                        └── [ ReplicaSet ]
                                                 │
                                                 └── [ Pod ]
                                                      ├── Container(s)
                                                      └── Volume(s)

  [ ConfigMap / Secret ] ───────► injected into Pods
  [ PVC ] ──► [ PV ] ───────────► mounted into Pods as persistent storage

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  1. The Infrastructure Layer
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Cluster.&lt;/strong&gt; The outer boundary — one control plane plus a pool of worker machines. Every other object lives inside it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Node.&lt;/strong&gt; A single worker machine, physical or virtual, running the kubelet agent. Nodes provide the raw CPU, memory, and disk that Pods get scheduled onto; they don't care which Namespace a Pod belongs to.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The Logical Layer
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Namespace.&lt;/strong&gt; A virtual partition for grouping and isolating resources — think environments (dev, staging, prod) or teams. Namespaces give you scoped names, per-team resource quotas, and RBAC boundaries, without needing separate clusters.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Networking
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Ingress.&lt;/strong&gt; The cluster's front door for HTTP/HTTPS. It routes external traffic to internal Services based on hostname or path, typically handling TLS termination along the way.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Service.&lt;/strong&gt; A stable IP and DNS name for a group of Pods. Pods are ephemeral — they get replaced, rescheduled, and change IPs — so the Service is the fixed address that Ingress and other Pods talk to.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Workload Controllers
&lt;/h2&gt;

&lt;p&gt;You almost never create Pods directly. Instead, you pick the controller that matches how your app should behave, and it manages Pods for you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deployment.&lt;/strong&gt; The default for stateless apps. Handles rolling updates, rollbacks, and scaling by managing a ReplicaSet underneath.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;StatefulSet.&lt;/strong&gt; For apps that need stable identity and durable storage — databases, queues, anything where &lt;code&gt;pod-0&lt;/code&gt; and &lt;code&gt;pod-1&lt;/code&gt; aren't interchangeable. Each Pod gets a predictable name and its own PVC.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DaemonSet.&lt;/strong&gt; Runs one Pod per Node (or a chosen subset). Use it for node-level agents: log shippers, metrics collectors, CNI plugins.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Job / CronJob.&lt;/strong&gt; A Job runs a Pod to completion for one-off work like migrations or batch processing. A CronJob is a Job on a schedule — the Kubernetes equivalent of &lt;code&gt;cron&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. The Execution Layer
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;ReplicaSet.&lt;/strong&gt; Keeps exactly N copies of a Pod running and replaces any that fail. You rarely manage these directly; a Deployment creates and updates them on your behalf.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pod.&lt;/strong&gt; The smallest deployable unit — one or more containers that share a network namespace (same IP) and a set of volumes. A Pod is the thing that actually gets scheduled onto a Node.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Container.&lt;/strong&gt; The running instance of your application image. Most Pods have one main container, sometimes with sidecars for logging, proxying, or similar helper tasks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Volume.&lt;/strong&gt; A directory available to the containers in a Pod, defined at the Pod level. It outlives individual container restarts and can be backed by anything from a temporary disk to a cloud volume.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Configuration and Storage
&lt;/h2&gt;

&lt;p&gt;These objects don't run code — they supply Pods with the data and storage they need.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ConfigMap.&lt;/strong&gt; Non-sensitive configuration — env vars, config files, feature flags — kept outside the container image so the same image runs anywhere.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Secret.&lt;/strong&gt; Same shape as a ConfigMap, but for sensitive values: API keys, passwords, TLS certs. Stored with tighter access controls and optionally encrypted at rest.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PersistentVolumeClaim (PVC) and PersistentVolume (PV).&lt;/strong&gt; A PV is a real piece of storage in the cluster (a cloud disk, an NFS share). A PVC is a Pod's request for storage — "I need 20Gi of SSD" — which Kubernetes binds to a matching PV. This indirection lets developers ask for storage without knowing the underlying infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It All Fits Together
&lt;/h2&gt;

&lt;p&gt;The payoff of this hierarchy is &lt;strong&gt;separation of concerns&lt;/strong&gt;, and it shows up clearly once you trace a request end to end:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;Deployment&lt;/strong&gt; declares &lt;em&gt;what&lt;/em&gt; should run.&lt;/li&gt;
&lt;li&gt;Its &lt;strong&gt;ReplicaSet&lt;/strong&gt; ensures &lt;em&gt;how many&lt;/em&gt; are running.&lt;/li&gt;
&lt;li&gt;Pods are placed onto Nodes — that's &lt;em&gt;where&lt;/em&gt; they run.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ingress&lt;/strong&gt; and &lt;strong&gt;Service&lt;/strong&gt; decide &lt;em&gt;how&lt;/em&gt; traffic reaches them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ConfigMaps&lt;/strong&gt;, &lt;strong&gt;Secrets&lt;/strong&gt;, and &lt;strong&gt;PVCs&lt;/strong&gt; provide &lt;em&gt;what they need&lt;/em&gt; — turning a generic image into a configured, stateful instance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the real payoff: your application is just a container. Everything around it — how it scales, how traffic reaches it, what config it reads, what storage it mounts — is handled by separate objects. Swap those objects and the same image runs anywhere, unchanged.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;One axis this post deliberately &lt;strong&gt;skips&lt;/strong&gt; is identity and access — ServiceAccounts, RBAC, and how cluster identity maps to cloud IAM. That would require a post of its own.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>kubernetes</category>
      <category>cloudnative</category>
      <category>distributedsystems</category>
      <category>go</category>
    </item>
    <item>
      <title>The Metadata Wall - Why Control Planes Break Before Data Planes Do</title>
      <dc:creator>Santosh Koti</dc:creator>
      <pubDate>Thu, 02 Apr 2026 01:50:51 +0000</pubDate>
      <link>https://dev.to/santosh_koti/the-metadata-wall-why-control-planes-break-before-data-planes-do-2mo9</link>
      <guid>https://dev.to/santosh_koti/the-metadata-wall-why-control-planes-break-before-data-planes-do-2mo9</guid>
      <description>&lt;p&gt;When building at &lt;strong&gt;massive&lt;/strong&gt; scale, "Data" is rarely the most complex part of the puzzle. Data is heavy, but it’s predictable. We have S3 for storage, NVMe for local speed, and bit-shoveling pipelines that can move petabytes.&lt;/p&gt;

&lt;p&gt;The real challenge is &lt;strong&gt;Metadata.&lt;/strong&gt; Metadata is the "Control Plane." It’s the routing table, the ownership lease, and the global state of the world. In a multi-datacenter (MDC) system, if your metadata layer hiccups, your data layer becomes a collection of expensive, unreachable zeros and ones.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. The Taxonomy: Map vs. Territory
&lt;/h2&gt;

&lt;p&gt;The simplest mental model is a &lt;strong&gt;Library.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Data is the books.&lt;/strong&gt; They are huge, they sit on shelves, and you rarely move them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Metadata is the card catalog.&lt;/strong&gt; It’s tiny, but it tells you exactly where every book is.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At this level of architecture, you realize that &lt;strong&gt;Metadata is the scaling bottleneck.&lt;/strong&gt; You can always add more "shelves" (Data nodes), but you eventually hit a wall on how fast you can update the "catalog" (Metadata store) while keeping it consistent across Virginia, Dublin, and Singapore.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. The Latency Floor: Paying the Speed of Light Tax
&lt;/h2&gt;

&lt;p&gt;At global scale, the speed of light isn't just a physics trivia point—it is the hard floor of your system's tail latency (P99).&lt;/p&gt;

&lt;p&gt;Metadata usually requires &lt;strong&gt;Strong Consistency.&lt;/strong&gt; You cannot have two different datacenters believing they both "own" the same user’s record. To prevent this, we use consensus protocols like Raft or Paxos. This means a write to the metadata layer is physically bound by the Round Trip Time (RTT) of a majority of your nodes.&lt;/p&gt;

&lt;p&gt;The math is unforgiving. Information in fiber-optic cables travels at roughly 2/3 the speed of light.&lt;/p&gt;

&lt;p&gt;A round-trip from New York to London (~11,000 km total distance) has a hard physical floor of &lt;strong&gt;~55ms&lt;/strong&gt; for every metadata commit. While your &lt;strong&gt;Data Plane&lt;/strong&gt; can cheat by using asynchronous replication (writing locally and syncing later), your &lt;strong&gt;Control Plane&lt;/strong&gt; (Metadata) cannot. It must pay this "Consensus Tax" in real-time to maintain a valid global state.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. The Architecture: Replicated Routing &amp;amp; The "Push" Model
&lt;/h2&gt;

&lt;p&gt;To scale effectively, we must decouple the &lt;strong&gt;Metadata Store&lt;/strong&gt; from &lt;strong&gt;Metadata Distribution.&lt;/strong&gt; If every data request had to query a central store like &lt;code&gt;etcd&lt;/code&gt; or &lt;code&gt;FoundationDB&lt;/code&gt; for a route, the coordination overhead would collapse the system. Instead, we use a "Push" model:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;The Source of Truth:&lt;/strong&gt; A small, strongly consistent cluster (The "Gold Copy").&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The Distribution Layer:&lt;/strong&gt; A sidecar or agent that "watches" the source of truth and streams updates to every router in the fleet via a gossip protocol or watch stream.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Local View:&lt;/strong&gt; Every router maintains an in-memory, $O(1)$ lookup table of the world.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This turns a "Global Network Trip" into a "Local Memory Lookup."&lt;/p&gt;

&lt;h3&gt;
  
  
  Handling the "Stale View"
&lt;/h3&gt;

&lt;p&gt;The trade-off is that some routers will inevitably be a few milliseconds behind. High-availability designs handle this via &lt;strong&gt;Self-Healing Loops&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If a router uses a stale metadata version and hits the wrong node, the node rejects the request with a &lt;strong&gt;Version Mismatch&lt;/strong&gt; or &lt;strong&gt;Wrong Shard&lt;/strong&gt; error.&lt;/li&gt;
&lt;li&gt;The router immediately invalidates its local cache and fetches the latest "Map" from the Control Plane.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  4. At a Glance: Metadata vs. Data
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature / Metric&lt;/th&gt;
&lt;th&gt;Metadata (Control Plane)&lt;/th&gt;
&lt;th&gt;Data (Data Plane)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Primary Goal&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Consistency (&lt;strong&gt;CP&lt;/strong&gt;)&lt;/td&gt;
&lt;td&gt;Availability (&lt;strong&gt;AP&lt;/strong&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scaling Dimension&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Coordination Overhead&lt;/td&gt;
&lt;td&gt;Throughput / Volume&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Access Pattern&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Extremely High Frequency&lt;/td&gt;
&lt;td&gt;High Volume, Lower Frequency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Storage Engine&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;etcd&lt;/code&gt;, &lt;code&gt;Zookeeper&lt;/code&gt;, &lt;code&gt;FoundationDB&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;S3, RocksDB, Cassandra&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Failure Result&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Global "Read-Only" or Outage&lt;/td&gt;
&lt;td&gt;Partial unavailability (Degraded)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  The Core Takeaway
&lt;/h2&gt;

&lt;p&gt;When designing multi-region systems, the primary question is: &lt;strong&gt;"Am I hitting the Metadata Wall?"&lt;/strong&gt; If coordination overhead is outgrowing data throughput, you don't need faster disks. You need to shard your control plane, push your routing tables to the edge, and use &lt;strong&gt;Hybrid Logical Clocks (HLCs)&lt;/strong&gt; to order events without waiting for global locks.&lt;/p&gt;




&lt;p&gt;Originally posted on: &lt;/p&gt;
&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://pomogomo.com/scaling_control_plane_metadata" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;pomogomo.com&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


</description>
      <category>distributedsystems</category>
      <category>cloud</category>
      <category>cloudstorage</category>
      <category>cloudnative</category>
    </item>
    <item>
      <title>Distributed Systems - Algebraic Types for Better State Modeling</title>
      <dc:creator>Santosh Koti</dc:creator>
      <pubDate>Wed, 01 Apr 2026 18:44:24 +0000</pubDate>
      <link>https://dev.to/santosh_koti/distributed-systems-algebraic-types-for-better-state-modeling-7bd</link>
      <guid>https://dev.to/santosh_koti/distributed-systems-algebraic-types-for-better-state-modeling-7bd</guid>
      <description>&lt;p&gt;Distributed systems are notoriously hard. They force us to deal with constant uncertainty: machines fail, networks drop messages, requests time out, and nodes unexpectedly change roles.&lt;/p&gt;

&lt;p&gt;Because of this, the real challenge in distributed architecture is not just writing business logic—it is &lt;strong&gt;modeling state correctly&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For example, a node in a cluster might be a leader, a follower, or in recovery. If we model these states poorly, subtle correctness bugs can appear quickly. This is exactly where &lt;strong&gt;algebraic types&lt;/strong&gt; shine.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Are Algebraic Types?
&lt;/h2&gt;

&lt;p&gt;The term sounds academic, but the idea is highly practical: algebraic types give us a structured way to model data.&lt;/p&gt;

&lt;p&gt;In everyday programming, this usually comes down to two things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Product types (structs):&lt;/strong&gt; data that belongs together&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sum types (enums):&lt;/strong&gt; values that can be &lt;em&gt;one&lt;/em&gt; of several valid states&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Does This Matter in Distributed Systems?
&lt;/h2&gt;

&lt;p&gt;Distributed systems have many valid states, and mixing them up leads to serious correctness problems.&lt;/p&gt;

&lt;p&gt;A node should never accidentally be both &lt;code&gt;Leader&lt;/code&gt; and &lt;code&gt;Recovering&lt;/code&gt; at the same time. If we model this with loose booleans like &lt;code&gt;is_leader = true&lt;/code&gt; and &lt;code&gt;is_recovering = true&lt;/code&gt;, invalid combinations can slip through. With a sum type like an enum, the system is forced to choose exactly one valid state.&lt;/p&gt;

&lt;p&gt;That makes the code easier to reason about and harder to misuse.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Simple Example in Rust
&lt;/h2&gt;

&lt;p&gt;Here is how that looks in practice:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// A product type: grouping related data&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Node&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;term&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// A sum type: defining mutually exclusive states&lt;/span&gt;
&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;Role&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Leader&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Follower&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Recovering&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// match forces us to handle every possible Role&lt;/span&gt;
    &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="py"&gt;.role&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nn"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Leader&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Node {} is the leader"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="py"&gt;.id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nn"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Follower&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Node {} is a follower"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="py"&gt;.id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nn"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Recovering&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Node {} is recovering"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="py"&gt;.id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This example is small, but it shows real architectural value:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;States are explicit:&lt;/strong&gt; there is no guessing what a node is doing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Invalid combinations are impossible:&lt;/strong&gt; a node cannot have two roles at once&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The compiler helps enforce correctness:&lt;/strong&gt; every valid case must be handled&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  The Takeaway
&lt;/h2&gt;

&lt;p&gt;Algebraic types matter in distributed systems because they help us model reality more clearly. In an environment that is already messy, clear state modeling is a foundation of correctness.&lt;/p&gt;

&lt;p&gt;Simply put: &lt;strong&gt;distributed systems are messy enough—your types should make your code clearer, not messier.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This post was originally published on:&lt;br&gt;
&lt;/p&gt;
&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://pomogomo.com/distributed_systems_algebraic_types" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;pomogomo.com&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



</description>
      <category>distributedsystems</category>
      <category>rust</category>
      <category>statemachine</category>
    </item>
    <item>
      <title>Distributed Systems - Quorum vs. Raft vs 2PC</title>
      <dc:creator>Santosh Koti</dc:creator>
      <pubDate>Wed, 01 Apr 2026 18:24:11 +0000</pubDate>
      <link>https://dev.to/santosh_koti/distributed-systems-quorum-vs-raft-vs-2pc-1847</link>
      <guid>https://dev.to/santosh_koti/distributed-systems-quorum-vs-raft-vs-2pc-1847</guid>
      <description>&lt;h2&gt;
  
  
  Quorum vs. Raft: The Hierarchy of Distributed Systems
&lt;/h2&gt;

&lt;p&gt;In distributed systems, we often confuse "how we store data" with "how we govern it." To build reliable systems, we must distinguish between a mathematical property (&lt;strong&gt;Quorum&lt;/strong&gt;), an orchestration protocol (&lt;strong&gt;Raft&lt;/strong&gt;), and a logical contract (&lt;strong&gt;ACID&lt;/strong&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The Functional Split
&lt;/h2&gt;

&lt;p&gt;The primary difference lies in which "Plane" the logic occupies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Quorum (The Data Plane Rule):&lt;/strong&gt; This is a mathematical requirement for &lt;strong&gt;Durability&lt;/strong&gt;. It defines how many nodes must acknowledge a piece of data to ensure it isn't lost ($W + R &amp;gt; N$). It is "blind"—it doesn't care who sends the data, only that it is stored safely.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Raft (The Full System Protocol):&lt;/strong&gt; Raft provides &lt;strong&gt;Ordering&lt;/strong&gt;. It tightly couples the &lt;strong&gt;Control Plane&lt;/strong&gt; (Who is the leader?) with the &lt;strong&gt;Data Plane&lt;/strong&gt; (Replicating the log). It ensures that every node sees the exact same sequence of events.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. The "Fencing" Problem
&lt;/h2&gt;

&lt;p&gt;These approaches differ most during a network failure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Raft (Built-in Protection):&lt;/strong&gt; Because Raft handles leadership and data, it has an internal &lt;strong&gt;fencing mechanism&lt;/strong&gt;. If a leader is partitioned, the protocol's "Terms" prevent it from committing more data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quorum (External Dependency):&lt;/strong&gt; In a &lt;strong&gt;pure Quorum system&lt;/strong&gt; (like Amazon Aurora storage), the storage doesn't know who the leader is. You need an &lt;strong&gt;external Control Plane&lt;/strong&gt; (AWS, Kubernetes, or a human) to "fence" or kill an old writer before they accidentally overwrite new data.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  3. The Hierarchy of Guarantees
&lt;/h2&gt;

&lt;p&gt;We can visualize these concepts as a stack. Each layer solves a progressively harder problem:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Primary Guarantee&lt;/th&gt;
&lt;th&gt;What it Solves&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ACID (The Peak)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Correctness&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Logical validity (e.g., bank transfers are safe and private).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Consensus (The Middle)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Ordering&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Agreement on a single, linear sequence of events.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Quorum (The Base)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Durability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Survival of data even if a minority of nodes fail.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  4. Scaling: Partitioned Consensus
&lt;/h2&gt;

&lt;p&gt;A single Raft group is a bottleneck. Modern systems like &lt;strong&gt;CockroachDB&lt;/strong&gt;, &lt;strong&gt;TiDB&lt;/strong&gt;, and &lt;strong&gt;Kafka&lt;/strong&gt; scale by breaking data into shards, where each shard runs its own independent consensus.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Multi-Raft:&lt;/strong&gt; Every 64MB of data is its own "City-State" with its own leader. This minimizes the "blast radius" of a failure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Two-Phase Commit (2PC):&lt;/strong&gt; When a transaction touches multiple shards, we use 2PC as the "Diplomatic Treaty." It ensures that Shard A and Shard B either commit together or fail together, providing &lt;strong&gt;Distributed ACID&lt;/strong&gt; on top of the Raft layer.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  5. Case Study: Kafka’s Hybrid Approach
&lt;/h2&gt;

&lt;p&gt;Kafka (KRaft mode) uses a clever "Brain vs. Muscle" distinction:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;The Metadata Quorum (The Brain):&lt;/strong&gt; A single Raft group for high-level cluster state.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Data Partitions (The Muscle):&lt;/strong&gt; A leaner protocol called &lt;strong&gt;ISR (In-Sync Replicas)&lt;/strong&gt; for the messages. This keeps the data plane fast by avoiding the "chatter" of thousands of independent Raft heartbeats.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Summary Comparison
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;System&lt;/th&gt;
&lt;th&gt;Control Plane (The Brain)&lt;/th&gt;
&lt;th&gt;Data Plane (The Muscle)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CockroachDB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Multi-Raft (Distributed)&lt;/td&gt;
&lt;td&gt;Multi-Raft (Distributed)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Kafka (KRaft)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Raft Quorum&lt;/td&gt;
&lt;td&gt;ISR Replication&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AWS Aurora&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;External Cluster Manager&lt;/td&gt;
&lt;td&gt;Storage Quorum ($W + R &amp;gt; N$)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  The Bottom Line: The Franchise Model
&lt;/h3&gt;

&lt;p&gt;Think of a global business:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Quorum&lt;/strong&gt; is the requirement for a sale (Customer pays + Register logs it).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Raft&lt;/strong&gt; is the Store Manager. They ensure the local shop is running in order.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Partitioning&lt;/strong&gt; is opening 1,000 different stores so no one shop is too crowded.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ACID&lt;/strong&gt; is Corporate HQ ensuring a "Gift Card" used in Store A is correctly deducted and accepted in Store B.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Quorum&lt;/strong&gt; gives you durability, &lt;strong&gt;Consensus&lt;/strong&gt; gives you ordering, and &lt;strong&gt;ACID&lt;/strong&gt; gives you correctness.&lt;/p&gt;

&lt;p&gt;This post was originally published on: &lt;br&gt;
&lt;/p&gt;
&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://pomogomo.com/distributed_systems_quorum_raft" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;pomogomo.com&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


</description>
      <category>distributedsystems</category>
      <category>database</category>
      <category>computerscience</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>Distributed Systems - Lamport Clock vs Hybrid Logical Clocks</title>
      <dc:creator>Santosh Koti</dc:creator>
      <pubDate>Wed, 01 Apr 2026 18:06:10 +0000</pubDate>
      <link>https://dev.to/santosh_koti/distributed-systems-lamport-clock-vs-hybrid-logical-clocks-aj1</link>
      <guid>https://dev.to/santosh_koti/distributed-systems-lamport-clock-vs-hybrid-logical-clocks-aj1</guid>
      <description>&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;In a distributed system — especially one spanning &lt;strong&gt;multiple regions and datacenters&lt;/strong&gt; — there's no single global clock. A node in Virginia and a node in Frankfurt each have their own clock, and they drift by milliseconds or more. Yet we need two guarantees:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Causal consistency&lt;/strong&gt; — if event A caused event B, every node in every datacenter must see A before B. A user in Tokyo shouldn't see a reply before the original message, just because their nearest replica processed the reply first.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Total ordering&lt;/strong&gt; — even for &lt;em&gt;unrelated&lt;/em&gt; events happening simultaneously in different regions, we need a deterministic way to put them in a single, agreed-upon order. Every node across every datacenter must agree, and they need to do it &lt;strong&gt;without a central coordinator&lt;/strong&gt; — fully decentralized.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The tricky part? Neither Lamport clocks nor HLCs can actually &lt;em&gt;detect&lt;/em&gt; concurrent events (Vector Clocks do that). But they don't need to — they solve a different problem. They assign every event a unique, sortable timestamp so you get a &lt;strong&gt;total order that respects causality&lt;/strong&gt;, even across nodes in different continents that never talk to each other directly.&lt;/p&gt;




&lt;h2&gt;
  
  
  Lamport Clocks — Logical Order Only
&lt;/h2&gt;

&lt;p&gt;A Lamport clock is just an integer counter. Two rules:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Before any local event or sending a message, increment your counter.&lt;/li&gt;
&lt;li&gt;When receiving a message, set your counter to &lt;code&gt;max(yours, theirs) + 1&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; A node in Virginia (clock=1) sends a write to a node in Frankfurt (clock=3). Frankfurt sets its clock to &lt;code&gt;max(3, 1) + 1 = 4&lt;/code&gt;. Now we know Frankfurt's event at 4 &lt;em&gt;happened after&lt;/em&gt; Virginia's event at 1 — even though these nodes are 6,000 km apart and their wall clocks disagree.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tie-breaking for total order:&lt;/strong&gt; When two events across different datacenters have the same Lamport timestamp, they're concurrent — but we still need to order them. The standard trick is to &lt;strong&gt;append the node ID&lt;/strong&gt;: &lt;code&gt;(timestamp, node_id)&lt;/code&gt;. So &lt;code&gt;(5, virginia-1)&lt;/code&gt; always comes before &lt;code&gt;(5, frankfurt-2)&lt;/code&gt; lexicographically. Arbitrary, but deterministic and consistent across all regions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The catch:&lt;/strong&gt; The timestamp "5" tells you nothing about wall-clock time. A user debugging a cross-region issue can't look at Lamport timestamps and say "this happened at 2:03 PM." You get ordering, not timing.&lt;/p&gt;




&lt;h2&gt;
  
  
  Hybrid Logical Clocks (HLC) — Best of Both Worlds
&lt;/h2&gt;

&lt;p&gt;An HLC is a tuple: &lt;strong&gt;(physical_time, logical_counter, node_id)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It tracks causality like a Lamport clock &lt;em&gt;and&lt;/em&gt; stays close to real wall-clock time — critical when your nodes are spread across regions with varying NTP sync quality.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;On a local event: use &lt;code&gt;max(your_physical, wall_clock)&lt;/code&gt;. If the physical part didn't advance, bump the logical counter. Otherwise reset it to 0.&lt;/li&gt;
&lt;li&gt;On receiving a message from another region: take &lt;code&gt;max(your_physical, their_physical, wall_clock)&lt;/code&gt;. If the max physical time didn't change, bump the logical counter. Otherwise reset to 0.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; A node in Virginia sends at physical time &lt;code&gt;10:00:01.000&lt;/code&gt;, HLC = &lt;code&gt;(10:00:01.000, 0, virginia-1)&lt;/code&gt;. A node in Singapore receives it, but Singapore's wall clock reads &lt;code&gt;10:00:00.500&lt;/code&gt; (behind due to NTP drift). Instead of going backward, Singapore's HLC becomes &lt;code&gt;(10:00:01.000, 1, singapore-1)&lt;/code&gt; — it takes the higher physical time and bumps the counter. Causality preserved, clock never goes backward, and the timestamp still reflects roughly &lt;em&gt;when&lt;/em&gt; it happened.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;HLC tie-breaking (three levels):&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Compare &lt;strong&gt;physical timestamps&lt;/strong&gt; — the later wall-clock time wins.&lt;/li&gt;
&lt;li&gt;If physical times are equal, compare &lt;strong&gt;logical counters&lt;/strong&gt; — higher counter means it saw more causal history at that timestamp.&lt;/li&gt;
&lt;li&gt;If both are equal, compare &lt;strong&gt;node IDs&lt;/strong&gt; — deterministic, arbitrary, but guarantees a total order even for truly simultaneous events in different datacenters.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This three-part comparison means every event globally — across every region — gets a unique, sortable position. No ambiguity, no central coordinator needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why production systems love this:&lt;/strong&gt; CockroachDB and YugabyteDB use HLCs across their geo-distributed clusters because they need causal ordering &lt;em&gt;without&lt;/em&gt; sacrificing the ability to reason about "when" something actually happened. When a user in London writes data replicated to nodes in São Paulo and Sydney, every replica agrees on the order — and an operator can still look at the timestamps and map them to real-world time.&lt;/p&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Lamport Clock&lt;/th&gt;
&lt;th&gt;Hybrid Clock&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Tracks causality?&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Detects concurrency?&lt;/td&gt;
&lt;td&gt;No (use Vector Clocks)&lt;/td&gt;
&lt;td&gt;No (use Vector Clocks)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reflects real time?&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Approximately&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tie-breaker&lt;/td&gt;
&lt;td&gt;Node ID&lt;/td&gt;
&lt;td&gt;Physical time → counter → node ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total order?&lt;/td&gt;
&lt;td&gt;Yes (with node ID)&lt;/td&gt;
&lt;td&gt;Yes (built-in)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Centralized?&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Works cross-region?&lt;/td&gt;
&lt;td&gt;Yes, but no time context&lt;/td&gt;
&lt;td&gt;Yes, with real-time context&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Size&lt;/td&gt;
&lt;td&gt;Integer + node ID&lt;/td&gt;
&lt;td&gt;Timestamp + counter + node ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use case&lt;/td&gt;
&lt;td&gt;Theory, simple systems&lt;/td&gt;
&lt;td&gt;Production geo-distributed databases&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Lamport clocks tell you &lt;em&gt;what depends on what&lt;/em&gt;. Hybrid clocks tell you that &lt;strong&gt;and&lt;/strong&gt; roughly &lt;em&gt;when&lt;/em&gt;. Neither detects concurrency — but both give you a fully decentralized total order that never violates causality, even across datacenters on opposite sides of the planet.&lt;/p&gt;

&lt;p&gt;This post was originally published on:&lt;br&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://pomogomo.com/distributed_systems_logical_clocks" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;pomogomo.com&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


</description>
      <category>distributedsystems</category>
      <category>computerscience</category>
      <category>database</category>
      <category>cockroach</category>
    </item>
  </channel>
</rss>
