<?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: Piyush Gupta</title>
    <description>The latest articles on DEV Community by Piyush Gupta (@piyush6348).</description>
    <link>https://dev.to/piyush6348</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%2F12063%2F16835832.jpeg</url>
      <title>DEV Community: Piyush Gupta</title>
      <link>https://dev.to/piyush6348</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/piyush6348"/>
    <language>en</language>
    <item>
      <title>Mastering Schema Evolution: Why Apache Avro is the King of Big Data (Part 2)</title>
      <dc:creator>Piyush Gupta</dc:creator>
      <pubDate>Sun, 05 Apr 2026 14:03:38 +0000</pubDate>
      <link>https://dev.to/piyush6348/mastering-schema-evolution-why-apache-avro-is-the-king-of-big-data-part-2-3987</link>
      <guid>https://dev.to/piyush6348/mastering-schema-evolution-why-apache-avro-is-the-king-of-big-data-part-2-3987</guid>
      <description>&lt;h2&gt;
  
  
  The Evolution Nightmare
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://dev.to/piyush6348/beyond-json-a-high-performance-guide-to-thrift-protocol-buffers-part-1-2nee"&gt;Part 1&lt;/a&gt;, we saw how Thrift and Protocol Buffers use numeric tags to shrink data. But in a real-world distributed system, you can’t upgrade every microservice at the same time. You will always have &lt;strong&gt;Old Code&lt;/strong&gt; talking to &lt;strong&gt;New Code&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This creates a massive problem: &lt;strong&gt;Schema Evolution&lt;/strong&gt;. If Service A adds a new field to its database, will Service B (running the old code) crash when it tries to read that data?&lt;/p&gt;

&lt;h3&gt;
  
  
  Forward vs. Backward Compatibility
&lt;/h3&gt;

&lt;p&gt;To build a resilient system, you must understand two concepts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Backward Compatibility:&lt;/strong&gt; New code can read data written by old code. (Essential when you update your "Readers" first).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Forward Compatibility:&lt;/strong&gt; Old code can read data written by new code. (Essential when you update your "Writers" first).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In Thrift and Protobuf, this is managed by &lt;strong&gt;Tags&lt;/strong&gt;. If a reader sees a tag it doesn't recognize, it simply ignores it. But what if you want to avoid tags entirely?&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Apache Avro: The "No Tag" Evolution
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Apache Avro&lt;/strong&gt; was created within the Hadoop ecosystem because Thrift wasn't a perfect fit for massive data files. Unlike Protobuf, Avro &lt;strong&gt;does not store tag numbers&lt;/strong&gt; or field types in the binary data. It only stores the raw values.&lt;/p&gt;

&lt;h3&gt;
  
  
  How it works: The Pairwise Resolution
&lt;/h3&gt;

&lt;p&gt;How does the reader know what the data is if there are no tags? &lt;/p&gt;

&lt;p&gt;Avro uses two schemas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Writer’s Schema:&lt;/strong&gt; The schema the application used when it sent the data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reader’s Schema:&lt;/strong&gt; The schema the receiving application expects.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When data is read, the Avro library looks at both schemas side-by-side. If the field order changed or a field was renamed, Avro "resolves" the difference by looking at the field names.&lt;/p&gt;

&lt;p&gt;![Diagram: Avro Reader and Writer Schema Resolution Logic]&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementation Example (Avro JSON Schema)
&lt;/h3&gt;

&lt;p&gt;Avro schemas are written in simple JSON, making them much easier to generate dynamically than the IDLs we saw in Part 1.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;user_schema.avsc&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"record"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"User"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"namespace"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"com.piyush.devto"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"fields"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"age"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"int"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"null"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"default"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"null"&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Python Implementation:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;avro.schema&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;avro.datafile&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;DataFileWriter&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;avro.io&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;DatumWriter&lt;/span&gt;

&lt;span class="c1"&gt;# 1. Load the schema from a file
&lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;avro&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user_schema.avsc&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rb&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="c1"&gt;# 2. Write binary data to an Avro Object Container File
&lt;/span&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;DataFileWriter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;users.avro&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wb&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nc"&gt;DatumWriter&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;writer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;username&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Piyush&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;age&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;email&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;piyush@example.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Data serialized to users.avro&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The Killer Feature: Database Integration
&lt;/h2&gt;

&lt;p&gt;One reason Avro is the "gold standard" for &lt;strong&gt;Kafka&lt;/strong&gt; and &lt;strong&gt;Big Data&lt;/strong&gt; is its relationship with relational databases.&lt;/p&gt;

&lt;p&gt;Because Avro schemas are JSON, you can write a script to automatically convert a SQL table into an Avro schema. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SQL Column&lt;/strong&gt; → Avro Field Name&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SQL Data Type&lt;/strong&gt; → Avro Type&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nullable Column&lt;/strong&gt; → Avro Union &lt;code&gt;["type", "null"]&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This makes Avro perfect for &lt;strong&gt;Change Data Capture (CDC)&lt;/strong&gt;, where you stream every single update from your Postgres or MySQL database into a Data Lake like S3 or Snowflake.&lt;/p&gt;




&lt;h2&gt;
  
  
  Summary: Choosing Your Weapon
&lt;/h2&gt;

&lt;p&gt;Which encoding should you use for your next project?&lt;/p&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;JSON&lt;/th&gt;
&lt;th&gt;Protobuf / Thrift&lt;/th&gt;
&lt;th&gt;Apache Avro&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Best For&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Public APIs&lt;/td&gt;
&lt;td&gt;Internal Microservices&lt;/td&gt;
&lt;td&gt;Big Data / Pipelines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Speed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Slowest&lt;/td&gt;
&lt;td&gt;Fast&lt;/td&gt;
&lt;td&gt;Fastest (no tags)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Schema&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Optional&lt;/td&gt;
&lt;td&gt;Required (IDL)&lt;/td&gt;
&lt;td&gt;Required (JSON)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Logic&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Human-readable&lt;/td&gt;
&lt;td&gt;Tag-based&lt;/td&gt;
&lt;td&gt;Resolution-based&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Final Thoughts
&lt;/h3&gt;

&lt;p&gt;Encoding isn't just about saving bytes; it's about defining the &lt;strong&gt;contract&lt;/strong&gt; between your services. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;JSON&lt;/strong&gt; when you need ease of use. &lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;Protobuf&lt;/strong&gt; for gRPC and internal speed. &lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;Avro&lt;/strong&gt; when your data is massive and your schemas are constantly evolving.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What are you using in production? Let's discuss in the comments!&lt;/strong&gt;&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

---

### Why this works for dev.to:
* **Series Link:** The `series` tag in the metadata automatically links Part 1 and Part 2 on the platform.
* **Liquid Tags:** I used blockquotes and bolded text to highlight "Forward/Backward" compatibility—a common interview question for senior devs.
* **Conclusion Table:** Dev.to readers love a quick "Cheat Sheet" or comparison table to wrap up a long-form post.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>bigdata</category>
      <category>architecture</category>
      <category>kafka</category>
      <category>programming</category>
    </item>
    <item>
      <title>Beyond JSON: A High-Performance Guide to Thrift &amp; Protocol Buffers (Part 1)</title>
      <dc:creator>Piyush Gupta</dc:creator>
      <pubDate>Sun, 05 Apr 2026 14:02:03 +0000</pubDate>
      <link>https://dev.to/piyush6348/beyond-json-a-high-performance-guide-to-thrift-protocol-buffers-part-1-2nee</link>
      <guid>https://dev.to/piyush6348/beyond-json-a-high-performance-guide-to-thrift-protocol-buffers-part-1-2nee</guid>
      <description>&lt;h2&gt;
  
  
  The "Text-Based" Performance Tax
&lt;/h2&gt;

&lt;p&gt;Most of us start our careers in the land of &lt;strong&gt;JSON&lt;/strong&gt;. It’s human-readable, it’s the language of the web, and it’s incredibly easy to debug. But as your system scales from a few hundred requests to hundreds of thousands per second, JSON starts to reveal its "tax."&lt;/p&gt;

&lt;h3&gt;
  
  
  Why JSON/XML is Killing Your Throughput:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Redundancy:&lt;/strong&gt; Every single message repeats the keys. If you send a list of 1,000 users, you are sending the string &lt;code&gt;"username"&lt;/code&gt; 1,000 times. That’s pure overhead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parsing Overhead:&lt;/strong&gt; Converting a string like &lt;code&gt;"12345.67"&lt;/code&gt; into a 64-bit float is a CPU-intensive operation. In high-performance systems, the time spent parsing JSON often exceeds the time spent processing the actual business logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Binary Inefficiency:&lt;/strong&gt; If you need to send binary data (like a profile picture or a byte array), you must use &lt;strong&gt;Base64&lt;/strong&gt; encoding, which increases the data size by approximately &lt;strong&gt;33%&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The Solution:&lt;/strong&gt; Binary Encoding. By using a schema-based binary format, we can strip away the field names and focus entirely on the data.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  1. Apache Thrift: The Multi-Protocol Powerhouse
&lt;/h2&gt;

&lt;p&gt;Originally developed at Facebook to solve cross-language service communication, &lt;strong&gt;Apache Thrift&lt;/strong&gt; is both an encoding format and a full RPC (Remote Procedure Call) framework.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Magic of the IDL
&lt;/h3&gt;

&lt;p&gt;Thrift uses an &lt;strong&gt;Interface Definition Language (IDL)&lt;/strong&gt;. Instead of defining your data in code, you define it in a &lt;code&gt;.thrift&lt;/code&gt; file. This acts as the single source of truth for all your services, whether they are written in Python, Go, or Java.&lt;/p&gt;

&lt;h3&gt;
  
  
  Binary Protocol vs. Compact Protocol
&lt;/h3&gt;

&lt;p&gt;Thrift offers different ways to "pack" your data:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Binary Protocol:&lt;/strong&gt; A simple, fast approach that encodes data in a straightforward binary format without much compression.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Compact Protocol:&lt;/strong&gt; This is where the efficiency shines. It uses &lt;strong&gt;Variable-length integers (Varints)&lt;/strong&gt;. For example, the number &lt;code&gt;7&lt;/code&gt; only takes 1 byte, while &lt;code&gt;7,000,000&lt;/code&gt; takes significantly more. It also packs field IDs and data types into a single byte whenever possible.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Implementation Example (Thrift IDL)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// user_profile.thrift
namespace java com.piyush.devto
namespace py devto.piyush

struct UserProfile {
  1: required string username,
  2: optional i32 age,
  3: optional bool is_active = true,
  4: optional list&amp;lt;string&amp;gt; tags
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Python Serialization Code:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;thrift.protocol&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TCompactProtocol&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;thrift.transport&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TTransport&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;devto.piyush.ttypes&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;UserProfile&lt;/span&gt;

&lt;span class="c1"&gt;# Creating a sample object
&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;UserProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Piyush&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;distributed-systems&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;backend&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="c1"&gt;# Step 1: Initialize a memory buffer
&lt;/span&gt;&lt;span class="n"&gt;transport&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TTransport&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TMemoryBuffer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Step 2: Use the Compact Protocol
&lt;/span&gt;&lt;span class="n"&gt;protocol&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TCompactProtocol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TCompactProtocol&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transport&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Step 3: Write (Serialize) the object to the buffer
&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;protocol&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Get the raw bytes
&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;transport&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getvalue&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Total encoded size: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; bytes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  2. Protocol Buffers (Protobuf): The Google Standard
&lt;/h2&gt;

&lt;p&gt;If you’ve ever looked into &lt;strong&gt;gRPC&lt;/strong&gt;, you’ve encountered &lt;strong&gt;Protocol Buffers&lt;/strong&gt;. Developed by Google, it is arguably the most popular binary encoding format in the industry today.&lt;/p&gt;

&lt;h3&gt;
  
  
  The "Tag" System: Why Order Matters
&lt;/h3&gt;

&lt;p&gt;In Protobuf, the &lt;em&gt;name&lt;/em&gt; of the field (&lt;code&gt;username&lt;/code&gt;) is never sent over the wire. Instead, Protobuf uses &lt;strong&gt;Tags&lt;/strong&gt; (unique numbers assigned to each field). &lt;/p&gt;

&lt;p&gt;When the encoder sees &lt;code&gt;string username = 1;&lt;/code&gt;, it simply writes: &lt;code&gt;[Field Tag 1] [Data Length] [Value]&lt;/code&gt;. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Critical Rule:&lt;/strong&gt; Once you assign a tag number (like &lt;code&gt;1&lt;/code&gt;), you can &lt;strong&gt;never&lt;/strong&gt; change it. If you change a tag, you break the ability for your services to understand each other.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Visualizing the Binary Layout
&lt;/h3&gt;

&lt;p&gt;While JSON looks like a mess of curly braces and quotes, a binary message looks like a streamlined stream of bits.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tag &amp;amp; Type&lt;/th&gt;
&lt;th&gt;Length/Value&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0x12&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0x06&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Field #1 is a String of 6 bytes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0x50 0x69 0x79...&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;"Piyush"&lt;/td&gt;
&lt;td&gt;The actual data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0x18&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0x19&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Field #2 is an Integer (25)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  Implementation Example (Protobuf)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight protobuf"&gt;&lt;code&gt;&lt;span class="na"&gt;syntax&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"proto3"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;devto&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;piyush&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;message&lt;/span&gt; &lt;span class="nc"&gt;UserProfile&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="na"&gt;username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;int32&lt;/span&gt; &lt;span class="na"&gt;age&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="na"&gt;is_active&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;repeated&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="na"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&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;&lt;strong&gt;Java Implementation:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Building the object&lt;/span&gt;
&lt;span class="nc"&gt;UserProfile&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;UserProfile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newBuilder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setUsername&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Piyush"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setAge&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setIsActive&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addTags&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"java"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addTags&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"grpc"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Serialization to byte array&lt;/span&gt;
&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;binaryData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toByteArray&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Deserialization on the other end&lt;/span&gt;
&lt;span class="nc"&gt;UserProfile&lt;/span&gt; &lt;span class="n"&gt;receivedUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;UserProfile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parseFrom&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;binaryData&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"User: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;receivedUser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getUsername&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Summary of Part 1
&lt;/h2&gt;

&lt;p&gt;By moving from JSON to a format like Thrift or Protobuf, you aren't just saving a few bytes. You are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Lowering Latency:&lt;/strong&gt; Binary data is parsed up to &lt;strong&gt;10x faster&lt;/strong&gt; than text.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Reducing Infrastructure Costs:&lt;/strong&gt; Less bandwidth means lower cloud egress bills.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Enforcing Type Safety:&lt;/strong&gt; Your API becomes a contract that cannot be easily broken.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Coming up in Part 2:&lt;/strong&gt; We will dive into &lt;strong&gt;Schema Evolution&lt;/strong&gt; (how to update your data without crashing your app) and why &lt;strong&gt;Apache Avro&lt;/strong&gt; is the undisputed king of Big Data and Kafka pipelines.&lt;/p&gt;

</description>
      <category>performance</category>
      <category>backend</category>
      <category>architecture</category>
      <category>programming</category>
    </item>
    <item>
      <title>B-Trees, Clustered Indexes, and the OLAP Revolution (Part 2) 📊</title>
      <dc:creator>Piyush Gupta</dc:creator>
      <pubDate>Sun, 05 Apr 2026 13:52:02 +0000</pubDate>
      <link>https://dev.to/piyush6348/b-trees-clustered-indexes-and-the-olap-revolution-part-2-4dj4</link>
      <guid>https://dev.to/piyush6348/b-trees-clustered-indexes-and-the-olap-revolution-part-2-4dj4</guid>
      <description>&lt;p&gt;In &lt;a href="https://dev.to/piyush6348/how-databases-actually-work-from-log-files-to-lsm-trees-part-1-joo"&gt;Part 1&lt;/a&gt;, we looked at LSM Trees—the write-heavy champions found in NoSQL databases. But if you’re using &lt;strong&gt;PostgreSQL, MySQL, or Oracle&lt;/strong&gt;, you’re likely interacting with a different beast: the &lt;strong&gt;B-Tree&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Today, we’ll explore why B-Trees still dominate the relational world and how the "Big Data" era forced us to rethink how we store rows entirely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table Of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;1. The King of RDBMS: B-Trees&lt;/li&gt;
&lt;li&gt;2. Clustered vs. Non-Clustered Indexes&lt;/li&gt;
&lt;li&gt;3. OLTP vs. OLAP: The Great Divide&lt;/li&gt;
&lt;li&gt;4. Why Column-Oriented Storage Wins at Scale&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  1. The King of RDBMS: B-Trees
&lt;/h2&gt;

&lt;p&gt;B-Trees are the most widely used indexing structure in history. Unlike the variable-size segments in LSM Trees, B-Trees break the database down into fixed-size &lt;strong&gt;pages&lt;/strong&gt; (usually 4KB to 16KB).&lt;/p&gt;

&lt;h3&gt;
  
  
  How it Works:
&lt;/h3&gt;

&lt;p&gt;A B-Tree is a balanced tree where each node contains multiple keys and pointers to child pages. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Root:&lt;/strong&gt; The entry point for every query.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leaf Nodes:&lt;/strong&gt; These contain the actual data or a reference to where the data lives on disk.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because the tree is always balanced, a B-Tree with a branching factor of 500 and just 4 levels can store &lt;strong&gt;256 billion rows&lt;/strong&gt;! This makes lookups incredibly consistent at $O(\log n)$.&lt;/p&gt;

&lt;h3&gt;
  
  
  B-Trees vs. LSM Trees: The Trade-off
&lt;/h3&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;B-Trees&lt;/th&gt;
&lt;th&gt;LSM Trees&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Best for&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Read-heavy workloads&lt;/td&gt;
&lt;td&gt;Write-heavy workloads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Fragmentation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High (due to empty page space)&lt;/td&gt;
&lt;td&gt;Low (sequential background merges)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Throughput&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Lower (multiple disk seeks)&lt;/td&gt;
&lt;td&gt;Higher (sequential appends)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  2. Clustered vs. Non-Clustered Indexes
&lt;/h2&gt;

&lt;p&gt;Where does the actual row live? This is a common interview question that boils down to index design:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Clustered Index:&lt;/strong&gt; The index &lt;strong&gt;is&lt;/strong&gt; the data. The leaf nodes of the B-Tree contain the actual row values. You can only have one clustered index per table (usually the Primary Key).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Non-Clustered (Secondary):&lt;/strong&gt; The index contains a "pointer" (like a Row ID) to the data's location. This allows for multiple indexes but requires an extra "hop" to fetch the full row.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  3. OLTP vs. OLAP: The Great Divide
&lt;/h2&gt;

&lt;p&gt;Most web developers spend their time in &lt;strong&gt;OLTP (Online Transaction Processing)&lt;/strong&gt;. You handle thousands of small queries: &lt;em&gt;"Update this user’s bio"&lt;/em&gt; or &lt;em&gt;"Add this item to the cart."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;However, businesses eventually need &lt;strong&gt;OLAP (Online Analytical Processing)&lt;/strong&gt;. This involves massive aggregate queries like: &lt;em&gt;"What was the total revenue in Q4 across all Asian markets?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Running these on your production database will cause it to crawl. Instead, we move data to a &lt;strong&gt;Data Warehouse&lt;/strong&gt; using &lt;strong&gt;ETL (Extract, Transform, Load)&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Why Column-Oriented Storage Wins at Scale
&lt;/h2&gt;

&lt;p&gt;Traditional databases store data &lt;strong&gt;Row-by-Row&lt;/strong&gt;. To calculate an average age, the DB loads the entire row (Name, Email, Bio, Password) just to access one tiny "Age" integer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Column-Oriented Storage&lt;/strong&gt; (BigQuery, Snowflake, ClickHouse) stores each column in its own file.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Code Reality:
&lt;/h3&gt;

&lt;p&gt;Imagine a table with 100 columns and 1 billion rows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Row-oriented: Reads 100 columns from disk per row.&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;AVG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Column-oriented: Reads ONLY the 'age' file. &lt;/span&gt;
&lt;span class="c1"&gt;-- It ignores the other 99 columns completely.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By only reading the necessary bytes, analytical queries that used to take hours now take seconds. Furthermore, because column data is often repetitive (e.g., many users living in the same "City"), these files compress significantly better than row-based data.&lt;/p&gt;




&lt;h2&gt;
  
  
  💬 Engineering Challenge
&lt;/h2&gt;

&lt;p&gt;Most modern architectures use &lt;strong&gt;Polyglot Persistence&lt;/strong&gt;—an RDBMS (B-Trees) for user transactions and a Data Warehouse (Columnar) for analytics.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Join the conversation:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Have you ever crashed a production DB by running a heavy "Analytical" query during peak hours?&lt;/li&gt;
&lt;li&gt;What’s your "Big Data" tool of choice—Snowflake, BigQuery, or maybe self-hosted ClickHouse?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Drop a comment below with your horror stories or favorite setups!&lt;/strong&gt; 🛠️✨&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>database</category>
      <category>sql</category>
      <category>bigdata</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>How Databases Actually Work: From Log Files to LSM Trees (Part 1) 🚀</title>
      <dc:creator>Piyush Gupta</dc:creator>
      <pubDate>Sun, 05 Apr 2026 13:49:52 +0000</pubDate>
      <link>https://dev.to/piyush6348/how-databases-actually-work-from-log-files-to-lsm-trees-part-1-joo</link>
      <guid>https://dev.to/piyush6348/how-databases-actually-work-from-log-files-to-lsm-trees-part-1-joo</guid>
      <description>&lt;p&gt;We often treat databases like PostgreSQL, MySQL, or MongoDB as magic black boxes. We send a query, and data comes back. But what is actually happening on the disk? &lt;/p&gt;

&lt;p&gt;If you've ever wondered why some databases are "fast for writes" while others are "fast for reads," the answer lies in the &lt;strong&gt;Storage Engine&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;In this post, we’re going to build a database from scratch and evolve it into a production-grade LSM Tree.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table Of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;1. The World’s Simplest Database&lt;/li&gt;
&lt;li&gt;2. Adding an Index (The Hash Map)&lt;/li&gt;
&lt;li&gt;3. Solving the Space Crisis: Compaction&lt;/li&gt;
&lt;li&gt;4. The Power of SSTables and LSM Trees&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  1. The World’s Simplest Database
&lt;/h2&gt;

&lt;p&gt;The simplest way to store data is to just append it to a text file. No complex schemas, just raw speed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Simple Key-Value Store in Bash&lt;/span&gt;
db_set&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; database.db
&lt;span class="o"&gt;}&lt;/span&gt;

db_get&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;# We use 'tail -n 1' to get the most recent update for that key&lt;/span&gt;
  &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s2"&gt;"^&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;,"&lt;/span&gt; database.db | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s2"&gt;"s/^&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;,//"&lt;/span&gt; | &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 1
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Verdict:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Writes:&lt;/strong&gt; &lt;strong&gt;O(1)&lt;/strong&gt; — Extremely fast. You just append to the end of the file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reads:&lt;/strong&gt; &lt;strong&gt;O(n)&lt;/strong&gt; — Terrible. To find one key, you have to scan the entire file from start to finish.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  2. Adding an Index (The Hash Map)
&lt;/h2&gt;

&lt;p&gt;To fix the $O(n)$ read problem, we use a &lt;strong&gt;Hash Index&lt;/strong&gt;. Think of this as a "Table of Contents" kept in your server's RAM.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Conceptual In-Memory Index
&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user_123&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="c1"&gt;# Byte offset in the file
&lt;/span&gt;  &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user_456&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user_789&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;128&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;seek&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_line&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is how &lt;strong&gt;Bitcask&lt;/strong&gt; (the default storage engine for Riak) works. It’s incredibly fast, but there’s a catch: &lt;strong&gt;All your keys must fit in RAM.&lt;/strong&gt; If you have billions of keys, your server will crash.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Solving the Space Crisis: Compaction
&lt;/h2&gt;

&lt;p&gt;Since we only append to our log, the file grows forever even if we're just updating the same key. Databases solve this via &lt;strong&gt;Compaction&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;We break the log into segments. Once a segment reaches a certain size, we close it and start a new one. A background process then merges these segments, throwing away old, overwritten values.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. The Power of SSTables and LSM Trees
&lt;/h2&gt;

&lt;p&gt;What if we store our data files sorted by key? This is a &lt;strong&gt;Sorted String Table (SSTable)&lt;/strong&gt;. Sorting allows us to merge segments efficiently (like Merge Sort) and perform range queries.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Modern Architecture:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Memtable:&lt;/strong&gt; All writes go to a balanced tree in memory (AVL or Red-Black Tree).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSTable:&lt;/strong&gt; When the Memtable gets too big, we flush it to disk as a sorted file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WAL (Write-Ahead Log):&lt;/strong&gt; Before writing to the Memtable, we append the operation to a "crash-recovery" log on disk.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;StorageEngine&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# 1. Append to WAL (for crash recovery)
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# 2. Add to Memtable
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;memtable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;memtable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_full&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;memtable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flush_to_sstable_on_disk&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;LSM Trees (Log-Structured Merge Trees)&lt;/strong&gt; are used by Cassandra, RocksDB, and LevelDB. They are the kings of write-heavy workloads!&lt;/p&gt;




&lt;h3&gt;
  
  
  💬 Let's Discuss!
&lt;/h3&gt;

&lt;p&gt;Have you ever run into a situation where your database writes were lagging? Did you realize your storage engine might be the bottleneck?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Question for the comments:&lt;/strong&gt; If you were building a high-frequency trading app with millions of updates per second, would you choose an LSM-based store or a traditional RDBMS? Why?&lt;/p&gt;

</description>
      <category>database</category>
      <category>backend</category>
      <category>architecture</category>
      <category>performance</category>
    </item>
    <item>
      <title>Master-Class: Understanding Database Replication (Single, Multi, and Leaderless)</title>
      <dc:creator>Piyush Gupta</dc:creator>
      <pubDate>Sun, 05 Apr 2026 13:39:36 +0000</pubDate>
      <link>https://dev.to/piyush6348/master-class-understanding-database-replication-single-multi-and-leaderless-hhm</link>
      <guid>https://dev.to/piyush6348/master-class-understanding-database-replication-single-multi-and-leaderless-hhm</guid>
      <description>&lt;p&gt;Database Replication is the process of keeping a copy of the same data on multiple nodes. Whether you are aiming for high availability, reduced latency, or horizontal scalability, choosing the right replication algorithm is critical.&lt;/p&gt;

&lt;p&gt;In this guide, we will explore the three primary algorithms used in modern distributed systems: &lt;strong&gt;Single Leader&lt;/strong&gt;, &lt;strong&gt;Multi-Leader&lt;/strong&gt;, and &lt;strong&gt;Leaderless&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Single Leader Replication&lt;/li&gt;
&lt;li&gt;Multi-Leader Replication&lt;/li&gt;
&lt;li&gt;Leaderless Replication&lt;/li&gt;
&lt;li&gt;The Replication Lag Problem&lt;/li&gt;
&lt;li&gt;Summary Comparison&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  1. Single Leader Replication
&lt;/h2&gt;

&lt;p&gt;This is the most common approach (used by MySQL, PostgreSQL, and MongoDB). One node is designated as the &lt;strong&gt;leader&lt;/strong&gt; (master), and all other nodes are &lt;strong&gt;followers&lt;/strong&gt; (read replicas).&lt;/p&gt;

&lt;h3&gt;
  
  
  How it Works
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Writes:&lt;/strong&gt; All write requests must be sent to the leader. The leader writes the data locally and sends the change to all followers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reads:&lt;/strong&gt; Clients can read from the leader or any follower.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Synchronous vs. Asynchronous
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Synchronous:&lt;/strong&gt; The leader waits for followers to confirm the write.

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Pros:&lt;/em&gt; Guaranteed consistency.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Cons:&lt;/em&gt; High latency; if one node fails, the whole write pipeline blocks.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Asynchronous:&lt;/strong&gt; The leader confirms the write immediately.

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Pros:&lt;/em&gt; High performance.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Cons:&lt;/em&gt; Risk of data loss if the leader fails before followers sync.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Handling Failures
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Follower Failure:&lt;/strong&gt; A follower "catches up" by using its local log to request missing data from the leader.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leader Failure (Failover):&lt;/strong&gt; Requires detecting failure via timeouts, electing a new leader, and reconfiguring the system.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  2. Multi-Leader Replication
&lt;/h2&gt;

&lt;p&gt;In this setup, more than one node can accept writes. This is typically used for applications spread across multiple geographic data centers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use Cases
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Multi-Data Center Operation:&lt;/strong&gt; Users write to the nearest data center to reduce latency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Offline Operation:&lt;/strong&gt; Apps like calendars or note-taking tools act as local "leaders" that sync with a server later.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Challenge: Conflict Resolution
&lt;/h3&gt;

&lt;p&gt;If two users edit the same data in different data centers simultaneously, a conflict occurs.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Conflict Avoidance:&lt;/strong&gt; Routing all writes for a specific record to the same leader.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Convergence:&lt;/strong&gt; Using &lt;strong&gt;Last Write Wins (LWW)&lt;/strong&gt; or &lt;strong&gt;Conflict-free Replicated Data Types (CRDTs)&lt;/strong&gt; to merge changes.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  3. Leaderless Replication
&lt;/h2&gt;

&lt;p&gt;Popularized by Amazon’s Dynamo, this approach allows &lt;strong&gt;any node&lt;/strong&gt; to accept writes and reads. Systems like Cassandra and Riak use this model.&lt;/p&gt;

&lt;h3&gt;
  
  
  Quorums ($n, w, r$)
&lt;/h3&gt;

&lt;p&gt;To maintain consistency without a leader, these systems use quorums:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;$n$: Total number of replicas.&lt;/li&gt;
&lt;li&gt;$w$: Nodes that must confirm a write.&lt;/li&gt;
&lt;li&gt;$r$: Nodes that must be queried for a read.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Rule:&lt;/strong&gt; For a successful read of the latest data, $w + r &amp;gt; n$.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Fixing Stale Data
&lt;/h3&gt;

&lt;p&gt;Since nodes can go down, systems fix stale data via:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Read Repair:&lt;/strong&gt; When a client detects an old version during a read, it pushes the newer value back to that node.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Anti-Entropy:&lt;/strong&gt; A background process that constantly syncs data between replicas.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  The Replication Lag Problem
&lt;/h2&gt;

&lt;p&gt;Regardless of the algorithm, asynchronous replication often results in "replication lag." To maintain a good user experience, developers should implement:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Read-Your-Own-Writes:&lt;/strong&gt; Ensures a user always sees the updates they just made.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monotonic Reads:&lt;/strong&gt; Ensures a user doesn't see data "disappear" when querying different replicas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistent Prefix Reads:&lt;/strong&gt; Guarantees that if writes happen in a specific order, they are read in that same order.&lt;/li&gt;
&lt;/ul&gt;




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

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Algorithm&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;th&gt;Main Downside&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Single Leader&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Read-heavy apps, general simplicity&lt;/td&gt;
&lt;td&gt;Leader is a single point of failure for writes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Multi-Leader&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Multi-region apps, offline capabilities&lt;/td&gt;
&lt;td&gt;Extremely complex conflict resolution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Leaderless&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High write throughput, high availability&lt;/td&gt;
&lt;td&gt;Complexities in eventual consistency&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>systemdesign</category>
      <category>backend</category>
      <category>database</category>
      <category>distributedsystems</category>
    </item>
  </channel>
</rss>
