<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Bkarali on Medium]]></title>
        <description><![CDATA[Stories by Bkarali on Medium]]></description>
        <link>https://medium.com/@b23karali?source=rss-33874b48d1ff------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/0*XFuJcdRqufZln2qf</url>
            <title>Stories by Bkarali on Medium</title>
            <link>https://medium.com/@b23karali?source=rss-33874b48d1ff------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 14 Nov 2026 16:43:42 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@b23karali/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Transactions: Where Consistency Gets Complicated]]></title>
            <link>https://medium.com/@b23karali/transactions-where-consistency-gets-complicated-5a9b45c9c6ca?source=rss-33874b48d1ff------2</link>
            <guid isPermaLink="false">https://medium.com/p/5a9b45c9c6ca</guid>
            <category><![CDATA[distributed-systems]]></category>
            <category><![CDATA[transactions]]></category>
            <category><![CDATA[system-design-concepts]]></category>
            <dc:creator><![CDATA[Bkarali]]></dc:creator>
            <pubDate>Sun, 20 Jul 2025 14:11:56 GMT</pubDate>
            <atom:updated>2025-07-20T18:19:46.620Z</atom:updated>
            <content:encoded><![CDATA[<p>Last week we talked about Partitioning. This week we cover the <strong>foundations of transactions</strong>, their guarantees, and what trade-offs system designers make when scaling them.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*yipL5x1EUa23CGwWYKVEwA.png" /><figcaption>Assembly line :: Atomicity (All or nothing)</figcaption></figure><h3>What is a Transaction?</h3><p>At its core, a <strong>transaction</strong> is a group of read/write operations that should be executed as <strong>one atomic unit</strong> — either all succeed or all fail.</p><p>Think:</p><ul><li>Transferring ₹1,000 from one bank account to another</li><li>Placing an e-commerce order and updating inventory</li><li>Publishing a blog post and tagging followers</li></ul><p>Without transactions, we risk <strong>inconsistency</strong>: partial updates, double-charges, or missing notifications.</p><h3>Single-Object vs Multi-Object Transactions</h3><p>Not all systems need full transactional semantics. Redis and DynamoDB offer atomic operations on single keys. But complex systems, like relational databases, often require coordination across multiple rows or tables.</p><p><strong>Example</strong>: Booking a seat and deducting balance requires transactional integrity across multiple tables. Without multi-object transactions, a system risks overbooking or inconsistent billing.</p><h3>Implementing Transactions: Write-Ahead Logging</h3><p>On a single node, transactions are typically backed by:</p><ul><li><strong>Write-Ahead Logs (WALs)</strong>: Changes are first written to a durable log before applying to the database.</li><li><strong>Undo logs</strong>: Roll back in-progress operations on failure.</li></ul><p>This mechanism ensures atomicity and durability, but doesn’t help with distributed coordination.</p><h3>ACID Guarantees</h3><p>The classic model is <strong>ACID</strong>:<br><strong>Atomicity: </strong>All or nothing — no partial state</p><p><strong>Consistency: </strong>Moves the DB from one valid state to another</p><p><strong>Isolation: </strong>Concurrent txns don’t interfere with each other</p><p><strong>Durability: </strong>Once committed, data survives crashes</p><p>But in real-world distributed systems, <strong>ACID is not binary</strong>. It’s a sliding scale — especially Isolation.</p><h3>Concurrency Control: Who Gets to Touch What and When</h3><p>Concurrency control ensures isolation and consistency under concurrent access.</p><h4>Pessimistic Concurrency (Locks)</h4><ul><li>Assumes conflicts are likely.</li><li>Transactions acquire locks on records/tables.</li><li>Prevents race conditions but causes blocking and potential deadlocks.</li></ul><h4>Optimistic Concurrency (MVCC)</h4><ul><li>Assumes conflicts are rare.</li><li>Transactions proceed without locks.</li><li>Validation occurs at commit time. If conflict is detected, one transaction is aborted.</li></ul><h3>Isolation Levels</h3><p>Real systems trade off isolation to boost performance:</p><p><strong>Read Uncommitted: </strong>Dirty reads — Very high-throughput systems<br><em>You read data that was </em><strong><em>written by an uncommitted transaction</em></strong><em>.<br>If that transaction rolls back, you’re left with </em><strong><em>invalid or inconsistent data</em></strong><em>.</em></p><p><strong>Read Committed:</strong> Non-repeatable reads — Most RDBMS defaults<br><em>You read the same row </em><strong><em>twice in the same transaction</em></strong><em>, but get </em><strong><em>different results</em></strong><em>.<br>This happens when another transaction </em><strong><em>modifies or deletes</em></strong><em> the row between your reads.</em></p><p><strong>Repeatable Read:</strong> Phantom reads — Financial systems<br><em>You run a query that returns a </em><strong><em>set of rows</em></strong><em>, then run it again and get </em><strong><em>new rows (phantoms)</em></strong><em>.<br> This is caused by another transaction </em><strong><em>inserting or deleting rows</em></strong><em> that match your query condition.</em></p><p><strong>Serializable:</strong> No anomalies, full isolation — Strict consistency needs</p><p>Serializable isolation ensures that <strong>concurrent transactions behave as if executed one after the other</strong> — eliminating anomalies like dirty reads, write skew, or phantoms. It’s ideal for correctness, but comes at a cost.</p><p>To guarantee serializability, systems need <strong>tight coordination across nodes</strong>, which introduces latency and complexity due to network delays and partial failures. Two common approaches:</p><ul><li><strong>Two-Phase Locking (2PL):</strong><br> Transactions <strong>lock all read/write data</strong> until commit.<br> Ensures serializability<br> Causes <strong>blocking, deadlocks</strong>, and <strong>reduced throughput</strong> in high-contention scenarios.</li><li><strong>Serializable Snapshot Isolation (SSI):</strong><br> Based on MVCC — each transaction works off a consistent snapshot.<br> Conflicts are <strong>detected at commit time</strong>, and transactions are aborted if needed.<br> More concurrency-friendly than 2PL<br> Still incurs <strong>abort overhead</strong> and <strong>tracking costs</strong>.</li></ul><h3>Distributed Challenges</h3><ul><li><strong>Global locks and consistent snapshots</strong> are hard to maintain across replicas.</li><li>Coordination protocols often <strong>violate availability or increase latency</strong>.</li><li>Systems like <strong>Spanner</strong> and <strong>CockroachDB</strong> solve this, but with complex clock or consensus designs.</li></ul><h3>Best Practices:</h3><ul><li>Don’t default to serializability — ask <strong>what guarantees your use case actually needs</strong>.</li><li>If you must use it, <strong>plan for coordination</strong>, <strong>handle contention</strong>, and <strong>monitor latency tradeoffs</strong>.</li><li>Often, <strong>weaker models (like Snapshot Isolation)</strong> offer a good balance of safety and scalability.</li></ul><h3>Distributed Transactions and 2PC</h3><p>In distributed systems, transactions often touch multiple nodes or services. To coordinate these, systems use protocols like:</p><h4>Two-Phase Commit (2PC)</h4><ol><li><strong>Prepare</strong>: Ask all participants if they’re ready to commit</li><li><strong>Commit</strong>: If all say yes, go ahead; else rollback</li></ol><p>But 2PC has problems:</p><ul><li>It’s <strong>blocking</strong> — if the coordinator crashes, participants get stuck</li><li><strong>No built-in rollback timeout</strong> — needs app-level recovery logic</li></ul><h3>Real-World Workarounds</h3><p>Most high-scale systems <strong>avoid distributed transactions</strong> in favor of:</p><ul><li><strong>Idempotent writes</strong> + <strong>retry logic</strong></li><li><strong>Compensating actions</strong> (undo logic, e.g., refund after a failed shipment)</li><li><strong>Eventual consistency</strong> via messaging/event queues (Kafka, SQS)</li><li><strong>Application-level sagas</strong> — a chain of local transactions coordinated by logic</li></ul><h3>Real-Life Examples</h3><ol><li><strong>Ride Sharing</strong>: Matching rider and driver, charging payment, and updating availability — often coordinated via retries and compensating actions, not strict transactions.</li><li><strong>Booking Systems</strong>: Airline or hotel bookings often use locks or temporary holds instead of full transactional isolation.</li><li><strong>Microservices</strong>: A checkout service talking to payment, inventory, and shipment services can’t use a shared DB — so it’s forced to coordinate loosely via events.</li></ol><h3>Key Takeaways</h3><ul><li><strong>Transactions</strong> are essential for correctness — but expensive across nodes.</li><li>In distributed systems, <strong>isolation is a trade-off</strong>, not a given.</li><li>Prefer <strong>local transactions</strong> + <strong>application-level orchestration</strong> (Sagas, retries).</li><li><strong>Know your consistency needs</strong> — sometimes, eventual is “good enough.”</li></ul><h3>Coming Up Next</h3><p>In the next post, we’ll dive into <strong>The Trouble with Distributed Consensus</strong> — the protocols and pain points behind keeping distributed systems in sync.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=5a9b45c9c6ca" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Partitioning: The Art of Breaking Things to Scale Them]]></title>
            <link>https://medium.com/@b23karali/partitioning-the-art-of-breaking-things-to-scale-them-1f3bbe520ba0?source=rss-33874b48d1ff------2</link>
            <guid isPermaLink="false">https://medium.com/p/1f3bbe520ba0</guid>
            <category><![CDATA[system-design-concepts]]></category>
            <category><![CDATA[partitioning]]></category>
            <category><![CDATA[data-science]]></category>
            <category><![CDATA[distributed-systems]]></category>
            <dc:creator><![CDATA[Bkarali]]></dc:creator>
            <pubDate>Sun, 13 Jul 2025 16:32:01 GMT</pubDate>
            <atom:updated>2025-07-13T16:32:01.701Z</atom:updated>
            <content:encoded><![CDATA[<p>In the first week, we looked at <strong>replication.</strong> This week, we explore its complementary concept: <strong>partitioning</strong>, the strategy that allows distributed systems to scale by <strong>dividing data</strong> — not duplicating it.</p><blockquote>If replication is about <strong>reliability</strong>, partitioning is about <strong>scalability</strong>.</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*pyR5edzkgyfz1RVuLn-38Q.png" /></figure><h3>Why Partition at All ?</h3><p>Partitioning — often called <strong>sharding</strong> — is essential when your system grows beyond what a single server can handle.</p><p>Without partitioning, you hit <strong>hard limits</strong> on:</p><ul><li><strong>Storage</strong> (e.g., disk size, memory)</li><li><strong>Throughput</strong> (CPU, IOPS, bandwidth)</li><li><strong>Fault isolation</strong> (a bad record takes down everything)</li></ul><p>Partitioning <strong>distributes both data and load</strong> across many machines. Each machine (or node) is responsible for only a subset (partition) of the data.</p><h3>Partitioning Basics</h3><h3>Partitioning by Key</h3><p>Every record has a <strong>partition key</strong>. The system uses this to route the data to a specific node.<br>Choose your key carefully — it affects: Load balancing, Query efficiency, Join and transaction complexity</p><p><strong>Example:</strong><br> If you’re building a ride-hailing app:</p><ul><li>Partition by city_id → geographic distribution</li><li>Partition by user_id → personalization queries</li><li>Partition by trip_id → useful for trip-centric writes</li></ul><h3>Common Strategies</h3><h4>a. Hash Partitioning</h4><ul><li>Applies a hash function to the key</li><li>Evenly spreads data across nodes</li><li>Used in <strong>Cassandra</strong>, <strong>MongoDB</strong>, <strong>Kafka partitions</strong></li></ul><p><strong>Good for</strong>: Write-heavy systems needing uniform load<br><strong>Limitation</strong>: Poor range-query support and tricky rebalancing</p><h4>b. Range Partitioning</h4><ul><li>Assigns ranges of keys to each partition (e.g., A–F, G–L)</li><li>Allows efficient range queries and sorted scans</li></ul><p>Used by systems like <strong>Google Bigtable, Spanner, HBase</strong></p><p><strong>Good for</strong>: Analytics, time-series queries<br><strong>Limitation</strong>: Skew if some ranges are hotter (e.g., recent timestamps)</p><h4>c. Directory-Based Partitioning</h4><ul><li>Maintains a key-to-node mapping in a separate service</li><li>Offers total flexibility in routing</li></ul><p>Used in older relational sharding architectures or custom systems.</p><p><strong>Good for</strong>: Complex routing, legacy systems<br><strong>Limitation</strong>: Adds a centralized dependency</p><h3>Real-World Challenges with Partitioning</h3><p>Partitioning isn’t just about splitting data — it’s about managing the <strong>complexity that follows</strong>.</p><h4>1. Hotspots and Skew</h4><p>Even with hash partitioning, certain users or keys can dominate traffic.</p><p><strong>Real-world example:</strong></p><ul><li>Partitioning temperature sensor data by timestamp causes all real-time writes to hit the same partition. This creates a write hotspot every minute, overwhelming the “current time” shard.</li><li>E-commerce flash sales may overload a partition if orders are partitioned by product ID.</li><li>Partitioning trending content by hashtag causes all posts with a viral tag (e.g., #WorldCup) to land on the same shard.</li></ul><h4>Solutions:</h4><ul><li>Add randomness to keys (e.g., userId#salt)</li><li>Time-bucketed partitioning (e.g., 2025-Q1#userId)</li></ul><h4>2. Rebalancing Data</h4><p>What happens when you add a new node?</p><p>In naive partitioning:</p><ul><li>You might need to <strong>re-shard everything</strong> (huge data movement)</li></ul><p>Systems like:</p><ul><li><strong>Cassandra</strong> and <strong>Dynamo</strong> use <strong>consistent hashing</strong> with virtual nodes to minimize rebalancing effort</li><li><strong>Bigtable</strong> and <strong>Spanner</strong> split range-based partitions dynamically under load</li></ul><p><strong>Design tip</strong>: Use virtual shards and dynamic mapping from the start.</p><h4>3. Secondary Indexing Across Partitions</h4><p>Partitioning optimizes lookups by the <strong>primary key</strong>. But what if you need to query by other fields?</p><p><strong>Example:</strong></p><ul><li>Partitioned by user_id, but need to search by email</li><li>Partitioned by trip_id, but need trips in a city</li></ul><h4>Options:</h4><ul><li><strong>Scatter-Gather</strong>: Query all partitions and merge results (expensive)</li><li><strong>Global Index</strong>: A separate index partitioned differently</li><li><strong>Denormalization</strong>: Duplicate data for query efficiency</li></ul><p><strong>Engineering Trade-off</strong>: Better performance vs. more complexity and consistency handling.</p><h4>4. Cross-Partition Joins and Transactions</h4><p>When related records are in <strong>different partitions</strong>, joins become <strong>distributed queries</strong>, and transactions need <strong>coordination</strong>.</p><p><strong>Real-world implications:</strong></p><ul><li>Joins across user → order → payment might span multiple nodes</li><li>Cross-shard transactions require <strong>2PC</strong>, which adds latency and potential for locks/failures</li></ul><h4>Mitigation:</h4><ul><li><strong>Co-locate related data</strong> in the same shard (e.g., customer + orders)</li><li><strong>Denormalize</strong> (store redundant data to reduce joins)</li><li><strong>Avoid distributed transactions</strong> where possible</li></ul><h3>Best Practices</h3><ol><li><strong>Choose a partition key that aligns with your access patterns</strong></li></ol><p>You don’t want to query by email if you partition by user_id.</p><p><strong>2. Design with future growth in mind</strong></p><ul><li>Use virtual shards</li><li>Expect rebalancing and automate it</li></ul><p><strong>3. Monitor partition health continuously</strong></p><ul><li>Skew and lag show up long after launch</li></ul><p><strong>4. Denormalize or duplicate wisely</strong></p><ul><li>Normalize for correctness, denormalize for scale</li></ul><p><strong>5. Accept that distributed joins and transactions are expensive</strong></p><ul><li>Try to stay within a shard boundary for critical flows</li></ul><p>As systems grow, your partitioning decisions age. What works at 100K users may break at 10M. <br>That’s why <em>partitioning should be </em><strong><em>revisited with scale</em></strong><em>, not just designed once and forgotten.</em></p><p>In the next post, we’ll move from how data is <strong>split</strong> to how it’s <strong>kept correct</strong>: distributed <strong>transactions</strong> — and why correctness, in a partitioned world, is harder than it looks.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1f3bbe520ba0" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Replication in Distributed Systems: Patterns, Pitfalls, and Practices : Part 1]]></title>
            <link>https://medium.com/@b23karali/replication-in-distributed-systems-patterns-pitfalls-and-practices-part-1-a139255a6efe?source=rss-33874b48d1ff------2</link>
            <guid isPermaLink="false">https://medium.com/p/a139255a6efe</guid>
            <category><![CDATA[system-design-concepts]]></category>
            <category><![CDATA[distributed-systems]]></category>
            <category><![CDATA[system-design-interview]]></category>
            <category><![CDATA[replication]]></category>
            <dc:creator><![CDATA[Bkarali]]></dc:creator>
            <pubDate>Sat, 05 Jul 2025 07:44:56 GMT</pubDate>
            <atom:updated>2025-07-05T08:03:31.038Z</atom:updated>
            <content:encoded><![CDATA[<h3>Replication in Distributed Systems: Patterns, Pitfalls, and Practices : Part 1</h3><p>In the world of distributed systems, <strong>replication</strong> is a cornerstone of reliability, availability, and performance. But with these benefits come a host of engineering trade-offs. This post distills key insights from the <em>Replication</em> chapter of <em>Designing Data-Intensive Applications</em> (DDIA), arguably one of the best books written on modern data systems.</p><figure><img alt="Rows of saplings which looks like replicas of each other" src="https://cdn-images-1.medium.com/max/1024/1*YkVfHXlzvz65aer-CYqKmg.png" /></figure><p>This post offers a high-level overview of replication. In the upcoming posts of this series, we’ll dive deeper into specific replication strategies, challenges, and real-world applications. <br>We’ll begin by covering key concepts at a broad level before exploring each topic in greater detail.</p><h3>Why Replicate Data?</h3><p>Replication means <strong>storing copies of the same data on multiple machines</strong>. The main goals are:</p><ul><li><strong>Fault Tolerance</strong>: If one node fails, another can take over.</li><li><strong>High Availability</strong>: Minimize downtime and serve read requests faster.</li><li><strong>Performance</strong>: Serve users closer to the data (geo-replication).</li></ul><h3>Replication Models</h3><h4>1. Single-Leader Replication (a.k.a. Primary-Replica)</h4><ul><li>One leader handles all writes.</li><li>Followers replicate data from the leader.</li><li><strong>Pros</strong>: Simple, strong consistency (if reads go to leader).</li><li><strong>Cons</strong>: Downtime during leader failure; can’t scale writes.</li></ul><h4>2. Multi-Leader Replication</h4><ul><li>Multiple nodes can accept writes.</li><li>Common in multi-region setups or offline-first apps (e.g., CRDTs or mobile sync).</li></ul><p><strong>Challenges</strong>:</p><ul><li>Write conflicts</li><li>Conflict resolution strategies (timestamps, last-write-wins, custom merge logic)</li></ul><h4>3. Leaderless Replication</h4><ul><li>No designated leader. Any node can accept writes.</li><li>Uses quorum-based consistency (e.g., read/write majority).</li><li>Found in systems like <strong>Amazon Dynamo</strong>, <strong>Cassandra</strong>.</li></ul><p><strong>Trade-offs</strong>:</p><ul><li>Complex conflict resolution</li><li>Higher consistency management burden on client or database</li></ul><h3>The Consistency Trade-off</h3><blockquote><em>“In a distributed system, consistency is not a yes/no question — it’s a spectrum.” — Martin Kleppmann</em></blockquote><p>Replication forces us to choose between:</p><ul><li><strong>Strong consistency</strong> (all replicas always agree)</li><li><strong>Eventual consistency</strong> (replicas may temporarily diverge)</li><li><strong>Tunable consistency</strong> (you choose R/W quorum levels, e.g., in Cassandra)</li></ul><p>This naturally ties into the <strong>CAP Theorem.</strong></p><h3>Pitfalls and Edge Cases</h3><p>Some challenges that arise in replicated systems:</p><ul><li><strong>Replication lag</strong>: Followers behind the leader can cause stale reads.</li><li><strong>Read Your Own Writes (RYOW)</strong> issues</li><li><strong>Write Conflicts</strong> in multi-leader and leaderless systems</li><li><strong>Split-brain scenarios</strong> in failovers</li><li><strong>Anti-entropy mechanisms</strong> to reconcile differences between nodes</li></ul><h3>Best Practices and Design Tips</h3><ul><li><strong>Know your consistency requirements</strong> before choosing a replication strategy.<br><em>Strong consistency</em><strong><em> </em></strong><em>: Bank transactions, stock trading, password updates.<br>Eventual consistency</em><strong><em> </em></strong><em>: News feed updates, analytics counters, product likes.</em></li><li>Use <strong>single-leader</strong> if you need strong consistency and your write load is low to moderate.<br><em>Thought: How would you design if consistency is of paramount importance and write load is heavy?</em></li><li>Use <strong>multi-leader</strong> for disconnected systems (e.g., mobile apps).<br><em>Best suited for </em><strong><em>offline-first</em></strong><em> or </em><strong><em>geo-distributed systems</em></strong><em> where users or regions need to </em><strong><em>write locally</em></strong><em> and sync later, collaboartive apps, POS systems in low connectivity environments.</em></li><li>Use <strong>leaderless</strong> if availability is paramount and eventual consistency is acceptable.<br><em>Leaderless systems are ideal for </em><strong><em>high-volume, high-availability workloads</em></strong><em>, but </em><strong><em>require discipline</em></strong><em> in modeling data and tuning consistency levels.</em></li><li>Be explicit about <strong>conflict resolution logic</strong> — don’t leave it to “last-write-wins” unless you’re sure it’s safe.<br><em>CRDTs for collaborative documents (e.g., real-time editors), Mergeable shopping carts or wishlist updates, Custom logic like “merge tags” instead of replacing them.</em></li><li>Monitor <strong>replication lag</strong> and handle stale reads carefully.<br><em>Profile update not reflecting immediately, Message sent but not appearing in chat UI. Stronger guarantees (read-your-writes) may require </em><strong><em>reading from the leader</em></strong><em>, increasing latency.</em></li></ul><blockquote>Replication is where performance, availability, and consistency collide. Knowing how to navigate that collision is what separates a reliable system from a fragile one.</blockquote><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a139255a6efe" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>