<script data-pm-proxy="intercept"></script><?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:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Hands On System Design - Distributed Systems Implementation ]]></title><description><![CDATA[Build production-grade distributed systems by writing every line of code yourself.  Build LogStream — your own production-grade distributed log platform. Hands on coding implementation everyday.]]></description><link>https://sdcourse.substack.com</link><image><url>https://substackcdn.com/image/fetch/$s_!zDvF!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7c554ddc-369a-4da8-bd75-54cd71f9a6e9_1024x1024.png</url><title>Hands On System Design - Distributed Systems Implementation </title><link>https://sdcourse.substack.com</link></image><generator>Substack</generator><lastBuildDate>Tue, 01 Sep 2026 09:12:28 GMT</lastBuildDate><atom:link href="/__u/sdcourse.substack.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Systemdr, Inc.]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[sdcourse@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[sdcourse@substack.com]]></itunes:email><itunes:name><![CDATA[System Design Course]]></itunes:name></itunes:owner><itunes:author><![CDATA[System Design Course]]></itunes:author><googleplay:owner><![CDATA[sdcourse@substack.com]]></googleplay:owner><googleplay:email><![CDATA[sdcourse@substack.com]]></googleplay:email><googleplay:author><![CDATA[System Design Course]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Day 74: Optimize Storage Format for Read/Write Patterns]]></title><description><![CDATA[Yesterday you put a cache in front of your query layer and watched p99s drop.]]></description><link>https://sdcourse.substack.com/p/day-74-optimize-storage-format-for</link><guid isPermaLink="false">https://sdcourse.substack.com/p/day-74-optimize-storage-format-for</guid><dc:creator><![CDATA[System Design Course]]></dc:creator><pubDate>Tue, 01 Sep 2026 08:30:36 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Kl3y!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6344c300-fe6e-4723-af98-8ffc4f8bf37b_4960x3280.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote><p>Yesterday you put a cache in front of your query layer and watched p99s drop. Good. But caching hides a storage problem, it doesn&#8217;t fix one. Today you fix the one underneath: your log events have been sitting in whatever format was convenient to write, and nobody asked whether that format was any good to read.</p></blockquote><p>Here&#8217;s what you&#8217;re building:</p><ul><li><p>A dual-tier storage engine &#8212; an append-only row store for fresh writes, and a columnar Parquet store for anything older than 15 minutes</p></li><li><p>A background compaction job that migrates segments from row to columnar format once their write activity cools off</p></li><li><p>A storage catalog in Postgres that tracks, per partition, which format it&#8217;s in and routes queries accordingly</p></li><li><p>A query router in the API gateway that picks hot or cold path based on the shape of the query, not just its time range</p></li></ul><h2>Why This Matters</h2><p>A write-heavy row format and a read-heavy columnar format are optimized for opposite access patterns, and most systems pick one and suffer for the other half of their traffic. LinkedIn&#8217;s Pinot and Uber&#8217;s original log pipeline both hit this: ingest was fast when everything was row-oriented and append-only, but a single &#8220;average latency by service, last 24 hours&#8221; query meant scanning full rows to read one field, over billions of rows. Netflix solved a version of this with their move to Iceberg &#8212; recent data lands in a write-optimized format, gets compacted into columnar Parquet on a schedule, and query engines never know the difference because a metadata layer tells them where to look. The pattern isn&#8217;t exotic. It&#8217;s just tiering by access pattern instead of by age for its own sake, though age happens to be a decent proxy for &#8220;nobody&#8217;s writing to this anymore.&#8221;</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!Kl3y!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6344c300-fe6e-4723-af98-8ffc4f8bf37b_4960x3280.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!Kl3y!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6344c300-fe6e-4723-af98-8ffc4f8bf37b_4960x3280.png 424w, /__u/substackcdn.com/image/fetch/$s_!Kl3y!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6344c300-fe6e-4723-af98-8ffc4f8bf37b_4960x3280.png 848w, /__u/substackcdn.com/image/fetch/$s_!Kl3y!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6344c300-fe6e-4723-af98-8ffc4f8bf37b_4960x3280.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Kl3y!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6344c300-fe6e-4723-af98-8ffc4f8bf37b_4960x3280.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!Kl3y!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6344c300-fe6e-4723-af98-8ffc4f8bf37b_4960x3280.png" width="1456" height="963" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6344c300-fe6e-4723-af98-8ffc4f8bf37b_4960x3280.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:963,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1299080,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://sdcourse.substack.com/i/210996621?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6344c300-fe6e-4723-af98-8ffc4f8bf37b_4960x3280.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!Kl3y!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6344c300-fe6e-4723-af98-8ffc4f8bf37b_4960x3280.png 424w, /__u/substackcdn.com/image/fetch/$s_!Kl3y!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6344c300-fe6e-4723-af98-8ffc4f8bf37b_4960x3280.png 848w, /__u/substackcdn.com/image/fetch/$s_!Kl3y!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6344c300-fe6e-4723-af98-8ffc4f8bf37b_4960x3280.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Kl3y!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6344c300-fe6e-4723-af98-8ffc4f8bf37b_4960x3280.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>System Design Deep Dive</h2><p><strong>Row-oriented vs. columnar, and why you need both.</strong> A row store puts everything for one record next to everything else for that record &#8212; good when you&#8217;re appending one record at a time and reading records mostly whole. A columnar store puts every service_name next to every other service_name, every latency_ms next to every other latency_ms. Terrible for single-record writes (you&#8217;re touching N different column files per insert), fantastic for &#8220;give me the p95 latency for service X&#8221; because you read one column, skip the other nineteen, and get compression ratios that make a bulk scan cheap. Trying to serve both patterns from one format means one of your workloads is always paying a tax it didn&#8217;t ask for.</p><p><strong>Compaction is the tax you pay instead.</strong> The trade you&#8217;re making is: writes stay cheap because they only touch the hot row store, and reads on old data stay cheap because a background process eventually reorganizes it &#8212; but that reorganization costs CPU, I/O, and a window where a segment is neither cleanly hot nor cleanly cold. Get the compaction trigger wrong (too aggressive) and you&#8217;re burning cycles converting segments that are still being written to. Too lazy, and your &#8220;cold&#8221; store balloons with row-format data that nobody optimized, and your columnar reads slow down because half of what they&#8217;re scanning isn&#8217;t actually columnar yet.</p><p><strong>Segment sizing determines your floor and ceiling.</strong> Too-small segments mean compaction runs constantly and Parquet&#8217;s columnar benefits never amortize &#8212; you pay per-file overhead (footer metadata, dictionary encoding setup) more often than you should. Too-large segments mean a segment stays &#8220;hot&#8221; and unqueryable-efficiently for longer, and a single slow compaction job blocks a bigger unit of data. We land on 15-minute, size-capped segments (128MB target) &#8212; small enough to compact fast, big enough that Parquet&#8217;s per-file overhead isn&#8217;t the dominant cost.</p><p><strong>Catalog-driven routing beats format-sniffing.</strong> You could have the query layer open a segment and figure out from its file extension or header what format it&#8217;s in. Don&#8217;t. That couples your query path to your storage internals and means every reader needs to understand both formats&#8217; quirks. Instead, a lightweight catalog table (<code>segment_id</code>, <code>partition_key</code>, <code>format</code>, <code>min_ts</code>, <code>max_ts</code>, <code>row_count</code>) tells the router where to look before it opens anything. This is the same idea behind Iceberg&#8217;s manifest files and Hudi&#8217;s timeline &#8212; metadata is cheap to query and lets you change storage internals without touching every consumer.</p><p><strong>Point lookups and aggregations need different routing logic entirely.</strong> A query for &#8220;show me log event abc-123&#8221; is a point lookup &#8212; go straight to hot store if it&#8217;s recent, or use a partition-pruned columnar scan if it&#8217;s old, either way touching one record&#8217;s worth of data. A query for &#8220;count errors by service over the last 6 hours&#8221; is an aggregation spanning both tiers &#8212; you query hot and cold separately and merge, because forcing one query path to handle both shapes means over-fetching for one of them. This is the mistake teams make most often here: they build one query interface and let it silently do a full scan for point lookups because the router only knows how to do range scans.</p>
      <p>
          <a href="/__u/sdcourse.substack.com/p/day-74-optimize-storage-format-for">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Week 13: API & Service Layer — The front door to your log platform]]></title><description><![CDATA[What we&#8217;re building today]]></description><link>https://sdcourse.substack.com/p/week-13-api-and-service-layer-the</link><guid isPermaLink="false">https://sdcourse.substack.com/p/week-13-api-and-service-layer-the</guid><dc:creator><![CDATA[System Design Course]]></dc:creator><pubDate>Sat, 29 Aug 2026 08:30:48 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!ycQw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F890868cb-6c98-4eee-9b0b-19db33cb12c5_4000x2400.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3><strong>What we&#8217;re building today</strong></h3><blockquote><p>Elasticsearch exposes logs through REST and GraphQL. Datadog ships SDKs so apps ingest without hand-crafting HTTP. Stripe rate-limits API keys by tier. Slack receives webhooks when events fire. Today you wire <strong>all seven access patterns</strong> into one platform.</p></blockquote><p><strong>By the end you&#8217;ll have:</strong></p><ul><li><p>Versioned REST API with search, stream, and stats</p></li><li><p>GraphQL for flexible log queries and mutations</p></li><li><p>Tiered rate limiting and daily quotas</p></li><li><p>Python and JavaScript SDKs plus a CLI</p></li><li><p>Webhook delivery on ERROR/WARN events</p></li><li><p>Batch ingest/query/delete for high throughput</p></li></ul><div><hr></div><h2><strong>Why this matters</strong></h2><blockquote><p>Your storage and analytics layers are useless if nothing can reach them safely. The API layer is the contract every client trusts &#8212; mobile apps, batch jobs, on-call engineers, and partner systems all enter here.</p></blockquote><div><hr></div><h2><strong>Core concepts</strong></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!c8_C!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9fd6e9c5-22c5-4db5-8745-91081e49915f_867x483.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!c8_C!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9fd6e9c5-22c5-4db5-8745-91081e49915f_867x483.png 424w, /__u/substackcdn.com/image/fetch/$s_!c8_C!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9fd6e9c5-22c5-4db5-8745-91081e49915f_867x483.png 848w, /__u/substackcdn.com/image/fetch/$s_!c8_C!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9fd6e9c5-22c5-4db5-8745-91081e49915f_867x483.png 1272w, /__u/substackcdn.com/image/fetch/$s_!c8_C!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9fd6e9c5-22c5-4db5-8745-91081e49915f_867x483.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!c8_C!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9fd6e9c5-22c5-4db5-8745-91081e49915f_867x483.png" width="867" height="483" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9fd6e9c5-22c5-4db5-8745-91081e49915f_867x483.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:483,&quot;width&quot;:867,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:71001,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://sdcourse.substack.com/i/201562984?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9fd6e9c5-22c5-4db5-8745-91081e49915f_867x483.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!c8_C!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9fd6e9c5-22c5-4db5-8745-91081e49915f_867x483.png 424w, /__u/substackcdn.com/image/fetch/$s_!c8_C!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9fd6e9c5-22c5-4db5-8745-91081e49915f_867x483.png 848w, /__u/substackcdn.com/image/fetch/$s_!c8_C!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9fd6e9c5-22c5-4db5-8745-91081e49915f_867x483.png 1272w, /__u/substackcdn.com/image/fetch/$s_!c8_C!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9fd6e9c5-22c5-4db5-8745-91081e49915f_867x483.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="callout-block" data-callout="true"><h2>Preparing for a distributed systems interview?</h2><p>&#8594;<a href="https://systemdrd.com/ebooks/sdcourse-distributed-systems-interview">Download the free Interview Pack</a></p><p>&#8594; <a href="/__u/sdcourse.substack.com/subscribe">Subscribe</a> now to access source code repository - 200 + coding lessons</p></div>
      <p>
          <a href="/__u/sdcourse.substack.com/p/week-13-api-and-service-layer-the">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Day 179: Device State Tracking and Management from IoT Logs]]></title><description><![CDATA[Week 26: IoT and Edge Log Processing Series: 254-Day Hands-On System Design with Distributed Log Processing System Implementation]]></description><link>https://sdcourse.substack.com/p/day-179-device-state-tracking-and</link><guid isPermaLink="false">https://sdcourse.substack.com/p/day-179-device-state-tracking-and</guid><dc:creator><![CDATA[System Design Course]]></dc:creator><pubDate>Thu, 27 Aug 2026 08:30:12 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!DVYN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e861b49-2537-4175-b71a-b2bea51e8890_3280x2240.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>What We Build Today</h2><ul><li><p>A device state machine that reconstructs current device status from raw logs</p></li><li><p>A state store that persists device state across restarts without a heavy database</p></li><li><p>A REST and WebSocket API for querying live device states</p></li><li><p>A browser dashboard showing real-time device health across a fleet</p></li></ul><p>This fits into Week 26&#8217;s IoT arc: Day 177 handled disconnected edge collection, Day 178 optimized bandwidth-constrained transport, and today we build the intelligence layer &#8212; knowing what state every device is in at any moment.</p><div><hr></div><h2>Why Device State Matters</h2><blockquote><p>Imagine managing 50,000 temperature sensors in cold-chain logistics. Each sensor streams logs every 30 seconds. A single log line says <code>{"device":"fridge-42","temp":8.2,"battery":12}</code>. But the question that matters to operations is not &#8220;what did fridge-42 just report?&#8221; &#8212; it is &#8220;what is the <em>current</em> state of fridge-42, and has anything changed since last check?&#8221;</p><p>That distinction &#8212; from raw event to current state &#8212; is what separates a log sink from an operational system.</p><p>Cloudflare&#8217;s IoT gateway team faces exactly this challenge managing millions of edge nodes. Their internal state service reconstructs device health from streamed telemetry, never relying on devices to explicitly announce state changes. State is inferred from the log stream itself.</p></blockquote><div><hr></div><h2>Core Concept: State Machines from Log Streams</h2><p>A device does not exist in a vacuum. At any moment it is in exactly one state: <code>online</code>, <code>degraded</code>, <code>offline</code>, <code>alert</code>, or <code>unknown</code>. Transitions between these states are driven by log events.</p><p>The core insight is that logs are <em>evidence</em> of state, not the state itself. Your system observes evidence and maintains a derived state model. This is called <strong>event-sourced state reconstruction</strong>.</p><pre><code><code>Log Event  &#8594;  State Transition Rules  &#8594;  Current State</code></code></pre><p>State transitions work as follows:</p><ul><li><p>A telemetry log within the expected interval keeps a device <code>online</code></p></li><li><p>A telemetry log with a battery value below threshold moves it to <code>degraded</code></p></li><li><p>No telemetry for more than 2x the expected interval moves it to <code>offline</code></p></li><li><p>A log with an error field or threshold breach moves it to <code>alert</code></p></li></ul><p>This mirrors how Apache Flink processes IoT streams at Alibaba &#8212; the stream processor holds a rolling state view for every device key, updated on each incoming record.</p><div><hr></div><h2>Architecture</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!DVYN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e861b49-2537-4175-b71a-b2bea51e8890_3280x2240.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!DVYN!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e861b49-2537-4175-b71a-b2bea51e8890_3280x2240.png 424w, /__u/substackcdn.com/image/fetch/$s_!DVYN!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e861b49-2537-4175-b71a-b2bea51e8890_3280x2240.png 848w, /__u/substackcdn.com/image/fetch/$s_!DVYN!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e861b49-2537-4175-b71a-b2bea51e8890_3280x2240.png 1272w, /__u/substackcdn.com/image/fetch/$s_!DVYN!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e861b49-2537-4175-b71a-b2bea51e8890_3280x2240.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!DVYN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e861b49-2537-4175-b71a-b2bea51e8890_3280x2240.png" width="520" height="355" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0e861b49-2537-4175-b71a-b2bea51e8890_3280x2240.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:994,&quot;width&quot;:1456,&quot;resizeWidth&quot;:520,&quot;bytes&quot;:543441,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://sdcourse.substack.com/i/210030755?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e861b49-2537-4175-b71a-b2bea51e8890_3280x2240.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!DVYN!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e861b49-2537-4175-b71a-b2bea51e8890_3280x2240.png 424w, /__u/substackcdn.com/image/fetch/$s_!DVYN!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e861b49-2537-4175-b71a-b2bea51e8890_3280x2240.png 848w, /__u/substackcdn.com/image/fetch/$s_!DVYN!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e861b49-2537-4175-b71a-b2bea51e8890_3280x2240.png 1272w, /__u/substackcdn.com/image/fetch/$s_!DVYN!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0e861b49-2537-4175-b71a-b2bea51e8890_3280x2240.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The system has four layers:</p><p><strong>Log Ingestion Layer</strong> &#8212; An HTTP endpoint accepts batches of device logs. Each log carries a device ID, timestamp, and payload. The ingestion layer validates and forwards to the state processor.</p><p><strong>State Processor</strong> &#8212; The central brain. For each incoming log, it applies transition rules, updates the state store, and emits a change event if the state actually changed. It also runs a background timer that checks for devices that have gone silent.</p><p><strong>State Store</strong> &#8212; A lightweight SQLite-backed store with an in-memory cache. Stores the current state, last seen timestamp, last log payload, and a transition history ring buffer (last 20 transitions per device).</p><p><strong>Query API + WebSocket Hub</strong> &#8212; A FastAPI server exposing <code>/devices</code>, <code>/devices/{id}</code>, and a WebSocket endpoint <code>/ws/state-changes</code> that pushes diffs to connected dashboards.</p><div><hr></div>
      <p>
          <a href="/__u/sdcourse.substack.com/p/day-179-device-state-tracking-and">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Day 73: Add Caching Layers for Frequent Queries]]></title><description><![CDATA[What We&#8217;re Building Today]]></description><link>https://sdcourse.substack.com/p/day-73-add-caching-layers-for-frequent-549</link><guid isPermaLink="false">https://sdcourse.substack.com/p/day-73-add-caching-layers-for-frequent-549</guid><dc:creator><![CDATA[System Design Course]]></dc:creator><pubDate>Tue, 25 Aug 2026 08:30:43 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!BxDr!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77ddfadc-fe72-4243-9f8a-e7a6c56b7c8d_2720x2960.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>What We&#8217;re Building Today</h2><ul><li><p><strong>Multi-tier cache architecture</strong> with L1 (in-process Caffeine) + L2 (Redis) for log query results</p></li><li><p><strong>Cache-aside pattern</strong> integrated into the log query pipeline with TTL and eviction strategies</p></li><li><p><strong>Cache warming</strong> on startup to pre-populate high-frequency query results</p></li><li><p><strong>Cache invalidation</strong> triggered by new log ingestion events via Kafka consumers</p></li></ul><div><hr></div><h2>Why This Matters</h2><p>In a distributed log processing system, the query layer is typically your hottest path. Logs are written once but queried many times &#8212; dashboards polling every 30 seconds, alerts re-evaluating the same time windows, and engineers repeatedly querying the same service/severity combinations during incidents.</p><p>Without caching, every query hits PostgreSQL. At modest scale (10K logs/sec), even a well-indexed DB begins buckling under the read amplification from dashboards and alerting pipelines. Netflix&#8217;s operational metrics infrastructure processes billions of log events daily &#8212; their query layer survives through aggressive caching at multiple levels, with cache hit rates above 95% for dashboard queries.</p><p>The architectural insight: <strong>read-heavy workloads don&#8217;t scale by optimizing the database &#8212; they scale by avoiding the database entirely for repeated queries.</strong></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!BxDr!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77ddfadc-fe72-4243-9f8a-e7a6c56b7c8d_2720x2960.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!BxDr!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77ddfadc-fe72-4243-9f8a-e7a6c56b7c8d_2720x2960.png 424w, /__u/substackcdn.com/image/fetch/$s_!BxDr!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77ddfadc-fe72-4243-9f8a-e7a6c56b7c8d_2720x2960.png 848w, /__u/substackcdn.com/image/fetch/$s_!BxDr!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77ddfadc-fe72-4243-9f8a-e7a6c56b7c8d_2720x2960.png 1272w, /__u/substackcdn.com/image/fetch/$s_!BxDr!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77ddfadc-fe72-4243-9f8a-e7a6c56b7c8d_2720x2960.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!BxDr!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77ddfadc-fe72-4243-9f8a-e7a6c56b7c8d_2720x2960.png" width="628" height="683.2087912087912" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/77ddfadc-fe72-4243-9f8a-e7a6c56b7c8d_2720x2960.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1584,&quot;width&quot;:1456,&quot;resizeWidth&quot;:628,&quot;bytes&quot;:855502,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://sdcourse.substack.com/i/208935037?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77ddfadc-fe72-4243-9f8a-e7a6c56b7c8d_2720x2960.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!BxDr!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77ddfadc-fe72-4243-9f8a-e7a6c56b7c8d_2720x2960.png 424w, /__u/substackcdn.com/image/fetch/$s_!BxDr!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77ddfadc-fe72-4243-9f8a-e7a6c56b7c8d_2720x2960.png 848w, /__u/substackcdn.com/image/fetch/$s_!BxDr!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77ddfadc-fe72-4243-9f8a-e7a6c56b7c8d_2720x2960.png 1272w, /__u/substackcdn.com/image/fetch/$s_!BxDr!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77ddfadc-fe72-4243-9f8a-e7a6c56b7c8d_2720x2960.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h2>System Design Deep Dive</h2><h3>1. Multi-Tier Cache Architecture</h3><p>The standard pattern in high-throughput systems is a two-level cache:</p><pre><code><code>QueryService &#8594; L1 Cache (Caffeine, in-process) &#8594; L2 Cache (Redis, distributed) &#8594; PostgreSQL
</code></code></pre><p><strong>L1 (Local/Heap Cache):</strong></p><ul><li><p>Sub-millisecond access, no network hop</p></li><li><p>Small capacity (512MB&#8211;1GB heap budget)</p></li><li><p>Invalidated on pod restart &#8212; not shared across instances</p></li><li><p>Best for: ultra-hot queries (top-10 dashboard panels)</p></li></ul><p><strong>L2 (Distributed Cache &#8212; Redis):</strong></p><ul><li><p>~1&#8211;2ms access over loopback/LAN</p></li><li><p>Shared across all API instances</p></li><li><p>Survives pod restarts; TTL-controlled</p></li><li><p>Best for: warm queries, session-scoped aggregations</p></li></ul><p><strong>Trade-off:</strong> Two-tier adds complexity. You now have two caches to invalidate and two failure modes. Accept this complexity only when L2 latency (~2ms) is still too high for your SLA. For most log platforms, L2 alone suffices until you&#8217;re serving 50K+ QPS.</p><p><strong>Anti-pattern:</strong> Never cache at both layers with <em>different</em> TTLs without a coherence strategy. If L1 TTL (30s) &gt; L2 TTL (10s), you&#8217;ll serve stale L1 data after L2 has been refreshed. L1 TTL must always be &#8804; L2 TTL.</p><div><hr></div><h3>2. Cache-Aside (Lazy Loading) Pattern</h3><p>Cache-aside is the dominant pattern for log query caching:</p><pre><code><code>1. Check L1 &#8594; hit: return immediately
2. Check L2 &#8594; hit: populate L1, return
3. Miss: query DB &#8594; populate L2 &#8594; populate L1 &#8594; return
</code></code></pre><p><strong>Why not write-through?</strong> Log ingestion is high-volume and write-through would cache every possible query permutation on write &#8212; combinatorially impossible. Cache-aside caches only what&#8217;s actually queried.</p><p><strong>Key insight:</strong> Cache key design is your most important architectural decision here. A poor key strategy leads to cache pollution (too many unique keys, low hit rate) or stale data (keys too coarse, wrong data returned).</p><pre><code><code>// Bad: too granular &#8212; each timestamp variation = cache miss
String key = "logs:" + serviceId + ":" + timestamp.toEpochMilli();

// Bad: too coarse &#8212; wrong results returned for different services
String key = "logs:recent";

// Good: query-shape based key with time bucketing
String key = String.format("logs:svc=%s:lvl=%s:bucket=%d",
    serviceId, level, timestamp.truncatedTo(ChronoUnit.MINUTES).toEpochSecond());
</code></code></pre><p>Time-bucketing (rounding timestamps to the nearest minute/5-minute window) dramatically improves hit rates for time-series log queries while keeping staleness bounded.</p><div><hr></div><h3>3. Cache Invalidation via Kafka</h3><p>The hardest problem in caching is knowing <em>when</em> to invalidate. In a log processing system, cache invalidation triggers when:</p><ol><li><p>New logs arrive for a cached service/time window</p></li><li><p>A log is retroactively tagged or reprocessed</p></li><li><p>TTL expiry (baseline safety net)</p></li></ol><p>The architectural pattern: use Kafka as the invalidation bus. Your <code>log-consumer</code> service already consumes log events &#8212; add a side-effect that publishes cache invalidation events to a <code>cache-invalidation</code> topic. The <code>api-gateway</code> subscribes and evicts matching keys.</p><pre><code><code>Kafka (log-events) &#8594; LogConsumer &#8594; [process log] + [publish to cache-invalidation topic]
                                                          &#8595;
                                              API Gateway (cache-invalidation consumer)
                                                          &#8595;
                                              Evict L1 + L2 keys for affected service/window
</code></code></pre><p><strong>Why not direct Redis DEL from the consumer?</strong> Because your consumer shouldn&#8217;t know about the cache topology of your query layer. Decoupling through Kafka means you can add a new caching tier without touching the consumer.</p><p><strong>Failure mode:</strong> If the cache-invalidation consumer lags, caches serve stale data. Mitigate with a short TTL (60&#8211;120s) as the safety net, so even if invalidation events are delayed, staleness is bounded.</p>
      <p>
          <a href="/__u/sdcourse.substack.com/p/day-73-add-caching-layers-for-frequent-549">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Week 6: Stream Processing with Kafka]]></title><description><![CDATA[Kafka Log Stream Processing at Production Scale]]></description><link>https://sdcourse.substack.com/p/week-6-stream-processing-with-kafka</link><guid isPermaLink="false">https://sdcourse.substack.com/p/week-6-stream-processing-with-kafka</guid><dc:creator><![CDATA[System Design Course]]></dc:creator><pubDate>Sat, 22 Aug 2026 07:51:38 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!YcxC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe5ae20a-85e5-4a27-ae60-ea5c272179f1_3840x2240.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3>Kafka Log Stream Processing at Production Scale</h3><blockquote><p>When log volume jumps from thousands to millions of events per second, a single database write path collapses. This platform treats Kafka as the durable backbone for multi-source ingestion, parallel consumption, duplicate-safe processing, compacted entity state, and live analytics&#8212;so operators see lag and throughput while the pipeline is still moving.</p></blockquote><h2>What We Built</h2><ul><li><p>A Spring Boot service that produces application logs, infrastructure metrics, and transactional events into dedicated Kafka topics, with an outbox path for reliable transaction publishes</p></li><li><p>Batch and partitioned consumers with enrichment, aggregation, sticky partition assignment, retries, and a dead-letter topic for poison messages</p></li><li><p>Transactional dual-topic produces plus cache-and-database idempotency so redeliveries do not create duplicate processed rows</p></li><li><p>Compacted entity-state topics with tombstones, materialization into JPA stores, and a Kafka Streams topology that feeds a WebSocket dashboard</p></li></ul><h2>Why This Matters</h2><p>At Netflix- or Datadog-scale observability, logs are not &#8220;files&#8221;&#8212;they are continuous streams that must fan out to enrichment, alerting, and analytics without blocking the write path. Uber-style ride telemetry and Amazon order pipelines hit the same constraint: producers must stay fast, consumers must scale horizontally by partition, and &#8220;processed once&#8221; must survive restarts. The anti-pattern is a shared consumer group with a single partition and at-most-once commits&#8212;throughput looks fine until a crash silently drops events or a rebalance doubles them. Partition keys, consumer groups, and explicit idempotency are how high-scale platforms keep both latency and correctness under load.</p><h2>System Design Deep Dive</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!YcxC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe5ae20a-85e5-4a27-ae60-ea5c272179f1_3840x2240.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!YcxC!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe5ae20a-85e5-4a27-ae60-ea5c272179f1_3840x2240.png 424w, /__u/substackcdn.com/image/fetch/$s_!YcxC!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe5ae20a-85e5-4a27-ae60-ea5c272179f1_3840x2240.png 848w, /__u/substackcdn.com/image/fetch/$s_!YcxC!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe5ae20a-85e5-4a27-ae60-ea5c272179f1_3840x2240.png 1272w, /__u/substackcdn.com/image/fetch/$s_!YcxC!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe5ae20a-85e5-4a27-ae60-ea5c272179f1_3840x2240.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!YcxC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe5ae20a-85e5-4a27-ae60-ea5c272179f1_3840x2240.png" width="1456" height="849" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/be5ae20a-85e5-4a27-ae60-ea5c272179f1_3840x2240.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:849,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:686326,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://sdcourse.substack.com/i/208788210?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe5ae20a-85e5-4a27-ae60-ea5c272179f1_3840x2240.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!YcxC!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe5ae20a-85e5-4a27-ae60-ea5c272179f1_3840x2240.png 424w, /__u/substackcdn.com/image/fetch/$s_!YcxC!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe5ae20a-85e5-4a27-ae60-ea5c272179f1_3840x2240.png 848w, /__u/substackcdn.com/image/fetch/$s_!YcxC!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe5ae20a-85e5-4a27-ae60-ea5c272179f1_3840x2240.png 1272w, /__u/substackcdn.com/image/fetch/$s_!YcxC!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe5ae20a-85e5-4a27-ae60-ea5c272179f1_3840x2240.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Four patterns carry the architecture.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://sdcourse.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Hands On System Design - Distributed Systems Implementation  is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p><strong>Multi-source producers with different durability knobs.</strong> Application-log producers can favor throughput (<code>acks=1</code>, lz4). Transactional and dual-topic paths use <code>acks=all</code> with producer idempotence. That split matches reality: not every shipper needs the same durability budget, but money-adjacent and audit events do.</p><p><strong>Partitioning and sticky assignment.</strong> Events keyed by source land on a multi-partition topic; consumers use sticky assignment and concurrency so parallel workers stay on warm partitions across rebalances. Lag metrics expose whether you need more consumers or better keys&#8212;not more threads on one hot partition.</p><p><strong>Exactly-once at the application boundary.</strong> Kafka transactions publish to <code>raw-logs</code> and <code>audit-logs</code> atomically. Consumers read with <code>isolation.level=read_committed</code>. An idempotency lock (<code>processing</code> &#8594; <code>processed</code>) plus a unique <code>eventId</code> in the database stops duplicates when the same record is redelivered. Streams itself uses <code>at_least_once</code>&#8212;a deliberate trade-off: windowed analytics tolerate recomputation better than inventory-style side effects do.</p><p><strong>Compaction and streams for state and visibility.</strong> Entity updates go to a compacted topic; deletes are null-value tombstones. Materializers upsert or clear JPA and cache. Kafka Streams builds one- and five-minute window stores for endpoint, status, and region counts, queried interactively and pushed over STOMP to the dashboard.</p><h2>Data Flow &amp; State</h2><h3>Data path</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!iuYs!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F533e3600-8c4c-4eef-8254-6a7d54611c24_3920x1280.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!iuYs!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F533e3600-8c4c-4eef-8254-6a7d54611c24_3920x1280.png 424w, /__u/substackcdn.com/image/fetch/$s_!iuYs!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F533e3600-8c4c-4eef-8254-6a7d54611c24_3920x1280.png 848w, /__u/substackcdn.com/image/fetch/$s_!iuYs!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F533e3600-8c4c-4eef-8254-6a7d54611c24_3920x1280.png 1272w, /__u/substackcdn.com/image/fetch/$s_!iuYs!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F533e3600-8c4c-4eef-8254-6a7d54611c24_3920x1280.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!iuYs!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F533e3600-8c4c-4eef-8254-6a7d54611c24_3920x1280.png" width="1456" height="475" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/533e3600-8c4c-4eef-8254-6a7d54611c24_3920x1280.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:475,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:307509,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://sdcourse.substack.com/i/208788210?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F533e3600-8c4c-4eef-8254-6a7d54611c24_3920x1280.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!iuYs!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F533e3600-8c4c-4eef-8254-6a7d54611c24_3920x1280.png 424w, /__u/substackcdn.com/image/fetch/$s_!iuYs!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F533e3600-8c4c-4eef-8254-6a7d54611c24_3920x1280.png 848w, /__u/substackcdn.com/image/fetch/$s_!iuYs!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F533e3600-8c4c-4eef-8254-6a7d54611c24_3920x1280.png 1272w, /__u/substackcdn.com/image/fetch/$s_!iuYs!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F533e3600-8c4c-4eef-8254-6a7d54611c24_3920x1280.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>HTTP ingest and scheduled generators publish into topics such as <code>application-logs</code>, <code>log-events-partitioned</code>, <code>raw-logs</code>, <code>entity-state-compacted</code>, and <code>log-events</code>. Batch consumers enrich and persist; failures after retries land on <code>logs-dlq</code>. Streams reads <code>log-events</code> and writes window aggregates to sink topics. Redis (or an in-memory fallback) holds aggregation counters and idempotency keys; H2 or PostgreSQL holds durable rows. Prometheus scrapes Actuator; the UI polls or subscribes for live KPIs.</p><h3>Lifecycle states</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!EX3D!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02e43196-6956-4db5-951b-474e820b8567_3440x1680.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!EX3D!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02e43196-6956-4db5-951b-474e820b8567_3440x1680.png 424w, /__u/substackcdn.com/image/fetch/$s_!EX3D!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02e43196-6956-4db5-951b-474e820b8567_3440x1680.png 848w, /__u/substackcdn.com/image/fetch/$s_!EX3D!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02e43196-6956-4db5-951b-474e820b8567_3440x1680.png 1272w, /__u/substackcdn.com/image/fetch/$s_!EX3D!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02e43196-6956-4db5-951b-474e820b8567_3440x1680.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!EX3D!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02e43196-6956-4db5-951b-474e820b8567_3440x1680.png" width="1456" height="711" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/02e43196-6956-4db5-951b-474e820b8567_3440x1680.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:711,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:436820,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://sdcourse.substack.com/i/208788210?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02e43196-6956-4db5-951b-474e820b8567_3440x1680.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!EX3D!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02e43196-6956-4db5-951b-474e820b8567_3440x1680.png 424w, /__u/substackcdn.com/image/fetch/$s_!EX3D!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02e43196-6956-4db5-951b-474e820b8567_3440x1680.png 848w, /__u/substackcdn.com/image/fetch/$s_!EX3D!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02e43196-6956-4db5-951b-474e820b8567_3440x1680.png 1272w, /__u/substackcdn.com/image/fetch/$s_!EX3D!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F02e43196-6956-4db5-951b-474e820b8567_3440x1680.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Outbox rows move <code>PENDING</code> &#8594; <code>SENT</code> on successful Kafka send, or <code>LOCAL</code> when Kafka is disabled. Idempotency keys transition acquire &#8594; <code>processing</code> &#8594; <code>processed</code>, with release on failure so retries can proceed. Compacted entities cycle update &#8594; materialize; tombstones remove DB and cache entries. Cluster health reports <code>HEALTHY</code>/<code>DEGRADED</code>; Streams reports running or rebalancing.</p><h2>Production Insight</h2><p>The architectural lesson is that &#8220;exactly once&#8221; is a <em>composition</em>, not a broker checkbox. Transactions, <code>read_committed</code>, idempotent keys, and unique constraints together close the duplicate window; Streams <code>at_least_once</code> is acceptable for dashboards. Teams that flip one flag and ignore application locks still double-count under redelivery.</p><h3>Github Link:</h3><p><a href="https://github.com/sysdr/sdc-java-p/tree/main/week_6_integrated_project/week_6_integrated_project">https://github.com/sysdr/sdc-java-p/tree/main/week_6_integrated_project/week_6_integrated_project</a></p><h2>Implementation Walkthrough</h2><h3>Prerequisites</h3><ul><li><p>Java 17+</p></li><li><p>Maven 3.8+</p></li><li><p>Docker (for Kafka, Zookeeper, Redis; optional PostgreSQL and Prometheus)</p></li></ul><h3>Build</h3><p>From the project root:</p><pre><code><code>mvn clean package
mvn test</code></code></pre><h3>Run</h3><p>Local mode (H2, no brokers&#8212;REST still works):</p><pre><code><code>mvn spring-boot:run</code></code></pre><p>With Kafka:</p><pre><code><code>docker compose up -d zookeeper kafka redis
mvn spring-boot:run -Dspring-boot.run.profiles=local,kafka
</code></code></pre><p>With Kafka Streams and the live dashboard path:</p><pre><code><code>docker compose up -d zookeeper kafka redis
mvn spring-boot:run -Dspring-boot.run.profiles=local,kafka,streams
</code></code></pre><p>Full stack profile (PostgreSQL + Redis + Streams):</p><pre><code><code>docker compose up -d
mvn spring-boot:run -Dspring-boot.run.profiles=docker
</code></code></pre><h3>Verify</h3><ul><li><p>App: </p></li></ul><p>http://localhost:8080/</p><ul><li><p>API index: <a href="http://localhost:8080/api">http://localhost:8080/api</a></p></li><li><p>Health: <a href="http://localhost:8080/actuator/health">http://localhost:8080/actuator/health</a></p></li><li><p>Trigger the in-app load test via the cluster admin load-test endpoint documented in <code>README.md</code></p></li><li><p>Run the dashboard stream pipeline action on the UI (<code>POST /api/dashboard/flow/run</code>)</p></li></ul><p>Success looks like: Actuator UP, non-zero producer/consumer counters after load, and dashboard KPIs updating over WebSocket when the streams profile is active.</p><h2>Working Code Demo:</h2><div id="youtube2-CXj47TCPFsg" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;CXj47TCPFsg&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/CXj47TCPFsg?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><h3>Observe</h3><ul><li><p>Prometheus: </p></li></ul><p>http://localhost:9090</p><ul><li><p> (scrape config in <code>monitoring/prometheus.yml</code> &#8594; <code>/actuator/prometheus</code> on port 8080)</p></li><li><p>Dashboard WebSocket: SockJS <code>/ws</code>, topic <code>/topic/metrics</code></p></li><li><p>Key signals: producer success/error timers, consumer success/failure, DLQ count, idempotency hit/miss, state update/delete, Streams store health</p></li></ul><h2>Closing</h2><p>You leave with a runnable map of how production log platforms combine Kafka durability, partition parallelism, transactional publishes, compaction-backed state, and stream analytics&#8212;and how to verify each layer under load rather than trusting a single configuration flag.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://sdcourse.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Hands On System Design - Distributed Systems Implementation  is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Day 178: Bandwidth-Efficient Log Transport for IoT Networks]]></title><description><![CDATA[Week 26: IoT and Edge Log Processing 254-Day Hands-On System Design with Distributed Log Processing System Implementation SystemDR LLP | systemdrd.com]]></description><link>https://sdcourse.substack.com/p/day-178-bandwidth-efficient-log-transport</link><guid isPermaLink="false">https://sdcourse.substack.com/p/day-178-bandwidth-efficient-log-transport</guid><dc:creator><![CDATA[System Design Course]]></dc:creator><pubDate>Thu, 20 Aug 2026 08:30:14 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!rC9A!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ee4a1b9-a9ca-41b4-8123-7d6f60ae18a8_3280x2240.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>What We Build Today</h2><ul><li><p>A log transport layer that compresses, batches, and prioritises log payloads before sending</p></li><li><p>Delta encoding so repeated log fields transmit only their changes</p></li><li><p>An adaptive scheduler that reads available bandwidth and adjusts batch size and interval in real time</p></li><li><p>A receiver endpoint that decompresses, reconstructs, and stores logs with delivery acknowledgement</p></li><li><p>A dashboard showing transport efficiency metrics: bytes saved, compression ratio, queue depth</p></li></ul><div><hr></div><h2>The Constrained-Network Reality</h2><p>Yesterday you built a collector that buffers logs when the network disappears. Today the network is back, but it is slow, metered, or shared with operational traffic. Sending raw JSON logs over a 50 kbps GPRS link is not just inefficient; it actively competes with the control messages your devices depend on.</p><p>Stripe&#8217;s global payment terminals face exactly this. Many operate over cellular data with carrier-imposed byte caps. Their log shipping layer reduces payload size by 70 to 80 percent using field-level delta encoding combined with per-batch zlib compression. The insight is that consecutive logs from the same device share most of their structure: the same service name, the same host, the same log level prefix. Only timestamps, counters, and occasional message text change between entries.</p><div><hr></div><h2>Core Concepts</h2><h3>Delta Encoding for Structured Logs</h3><p>Rather than transmitting each log as an independent JSON object, you record a reference frame and transmit only the fields that differ from it. A hundred consecutive INFO logs from <code>payment-service</code> on <code>device-42</code> may share seven of nine fields. You send those seven fields once, then only two values per subsequent log.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!rC9A!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ee4a1b9-a9ca-41b4-8123-7d6f60ae18a8_3280x2240.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!rC9A!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ee4a1b9-a9ca-41b4-8123-7d6f60ae18a8_3280x2240.png 424w, /__u/substackcdn.com/image/fetch/$s_!rC9A!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ee4a1b9-a9ca-41b4-8123-7d6f60ae18a8_3280x2240.png 848w, /__u/substackcdn.com/image/fetch/$s_!rC9A!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ee4a1b9-a9ca-41b4-8123-7d6f60ae18a8_3280x2240.png 1272w, /__u/substackcdn.com/image/fetch/$s_!rC9A!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ee4a1b9-a9ca-41b4-8123-7d6f60ae18a8_3280x2240.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!rC9A!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ee4a1b9-a9ca-41b4-8123-7d6f60ae18a8_3280x2240.png" width="1456" height="994" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5ee4a1b9-a9ca-41b4-8123-7d6f60ae18a8_3280x2240.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:994,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:625487,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://sdcourse.substack.com/i/208648891?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ee4a1b9-a9ca-41b4-8123-7d6f60ae18a8_3280x2240.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!rC9A!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ee4a1b9-a9ca-41b4-8123-7d6f60ae18a8_3280x2240.png 424w, /__u/substackcdn.com/image/fetch/$s_!rC9A!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ee4a1b9-a9ca-41b4-8123-7d6f60ae18a8_3280x2240.png 848w, /__u/substackcdn.com/image/fetch/$s_!rC9A!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ee4a1b9-a9ca-41b4-8123-7d6f60ae18a8_3280x2240.png 1272w, /__u/substackcdn.com/image/fetch/$s_!rC9A!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ee4a1b9-a9ca-41b4-8123-7d6f60ae18a8_3280x2240.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Adaptive Batching</h3><p>Fixed batch intervals waste bandwidth when logs are sparse and cause latency spikes when logs are dense. An adaptive scheduler measures the rolling average log rate and adjusts the flush interval so each batch targets a configurable payload budget, say 4 KB compressed. When the network is fast, smaller intervals keep latency low. When the link degrades, intervals widen automatically.</p><h3>Priority Lanes</h3><p>Not all logs justify bandwidth. ERROR and CRITICAL logs ship immediately on a high-priority lane with no batching. INFO and DEBUG logs enter the batching pipeline and may be held for seconds or shed entirely when queue depth exceeds a threshold. This mirrors how Cloudflare&#8217;s edge nodes handle telemetry: security events bypass the batch queue entirely and stream to the ingestion cluster as they occur.</p><h3>Compression Strategy</h3><p>zlib level 6 is the practical sweet spot for IoT: it achieves 60 to 75 percent compression on JSON log streams at roughly 1 MB/s throughput on a Raspberry Pi-class CPU. LZ4 is faster but compresses 15 to 20 percent less. For payloads under 200 bytes, skip compression entirely since the zlib header overhead cancels the gain.</p>
      <p>
          <a href="/__u/sdcourse.substack.com/p/day-178-bandwidth-efficient-log-transport">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Day 72: Adaptive Batching Based on System Load]]></title><description><![CDATA[What We&#8217;re Building Today]]></description><link>https://sdcourse.substack.com/p/day-72-adaptive-batching-based-on</link><guid isPermaLink="false">https://sdcourse.substack.com/p/day-72-adaptive-batching-based-on</guid><dc:creator><![CDATA[System Design Course]]></dc:creator><pubDate>Tue, 18 Aug 2026 08:27:22 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!4rcP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b924285-cb9b-4c7f-b38e-1fe72d930e37_4400x2880.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>What We&#8217;re Building Today</h2><ul><li><p>A <strong>self-tuning batch controller</strong> that reads live system metrics and continuously adjusts Kafka consumer batch sizes</p></li><li><p>A <strong>load monitoring service</strong> synthesizing CPU load, heap usage, consumer lag, and processing latency into a single actionable load score</p></li><li><p>A <strong>feedback control loop</strong> wiring metrics collection &#8594; load scoring &#8594; batch size adjustment &#8594; effect measurement</p></li><li><p><strong>Micrometer gauges</strong> exposing batch size dynamics so Grafana shows the system tuning itself in real time</p></li></ul><div><hr></div><h2>Why This Matters</h2><p>Yesterday you profiled the ingestion pipeline and found where time goes. Today we fix the most insidious bottleneck that profiling alone cannot reveal: <strong>the batch size you chose at deployment time is wrong by definition</strong>.</p><p>A static batch of 500 records performs beautifully at 5% CPU load. At 85% CPU &#8212; during a traffic spike, a GC storm, or a co-located service competing for cores &#8212; that same batch size turns your consumer into a latency landmine. Records queue up in Kafka, end-to-end latency inflates, and the downstream SLA breach arrives before any alert fires.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://sdcourse.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Hands On System Design - Distributed Systems Implementation  is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>Netflix&#8217;s Mantis platform, Uber&#8217;s Kafka infrastructure team, and LinkedIn&#8217;s original Kafka consumers all converged on the same lesson: <strong>the optimal batch size is a function of current system state, not a constant</strong>. Static configuration is a performance anti-pattern disguised as simplicity.</p><p>Adaptive batching is the practical application of control theory to distributed systems. You are building a feedback loop &#8212; not writing configuration. That shift in mental model is the architectural insight this lesson delivers.</p><div><hr></div><h2>System Design Deep Dive</h2><h3>1. The Failure Mode of Static Batching</h3><p>Static batch sizes embed a hidden assumption: the system operating point does not change. In production, it changes constantly. Batch sizing involves three competing objectives:</p><p>Objective Prefers Why Throughput Large batches Fewer broker round-trips, better compression ratios Latency Small batches Records processed sooner, lower end-to-end delay Stability Small batches Prevents OOM, GC pressure, processing timeouts under load</p><p>Static configuration can optimize for one operating point. Adaptive batching tracks the Pareto frontier across all three as conditions change.</p><p><strong>Anti-pattern to avoid</strong>: picking a &#8220;safe&#8221; small batch size and calling it done. A batch of 10 records at 5% load processes ~10x fewer records per poll cycle than necessary, starving throughput and inflating Kafka consumer lag for no reason.</p><h3>2. The Feedback Control Architecture</h3><p>Adaptive batching is a closed-loop control system. The output (batch size) influences the system state (load), which feeds back into the controller input (load score):</p><pre><code><code>[JVM + Kafka Metrics] &#8594; [LoadMonitorService] &#8594; [load score: 0.0&#8211;1.0]
                                                          &#9474;
[current batch size] &#8592; [AdaptiveBatchController] &#8592;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;
         &#9474;
[Kafka Consumer] &#9472;&#9472;&#8594; [BatchMetrics] &#9472;&#9472;&#8594; feeds back into LoadMonitor</code></code></pre><p>The controller has three key parameters:</p><ul><li><p><strong>MIN_BATCH_SIZE</strong> (floor, e.g. 10): prevents throughput collapse under extreme load</p></li><li><p><strong>MAX_BATCH_SIZE</strong> (ceiling, e.g. 500): prevents memory exhaustion during low-load periods</p></li><li><p><strong>Smoothing factor &#945;</strong> (e.g. 0.30): exponential moving average that prevents oscillation</p></li></ul><p>Proportional-only control without smoothing causes oscillation &#8212; the system overshoots, corrects too aggressively, overshoots again. The smoothing factor damps response and prevents thrashing between batch sizes on every poll cycle. Think of it as the derivative term in a PID controller.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!4rcP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b924285-cb9b-4c7f-b38e-1fe72d930e37_4400x2880.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!4rcP!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b924285-cb9b-4c7f-b38e-1fe72d930e37_4400x2880.png 424w, /__u/substackcdn.com/image/fetch/$s_!4rcP!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b924285-cb9b-4c7f-b38e-1fe72d930e37_4400x2880.png 848w, /__u/substackcdn.com/image/fetch/$s_!4rcP!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b924285-cb9b-4c7f-b38e-1fe72d930e37_4400x2880.png 1272w, /__u/substackcdn.com/image/fetch/$s_!4rcP!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b924285-cb9b-4c7f-b38e-1fe72d930e37_4400x2880.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!4rcP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b924285-cb9b-4c7f-b38e-1fe72d930e37_4400x2880.png" width="1456" height="953" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5b924285-cb9b-4c7f-b38e-1fe72d930e37_4400x2880.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:953,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1767171,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://sdcourse.substack.com/i/208657867?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b924285-cb9b-4c7f-b38e-1fe72d930e37_4400x2880.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!4rcP!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b924285-cb9b-4c7f-b38e-1fe72d930e37_4400x2880.png 424w, /__u/substackcdn.com/image/fetch/$s_!4rcP!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b924285-cb9b-4c7f-b38e-1fe72d930e37_4400x2880.png 848w, /__u/substackcdn.com/image/fetch/$s_!4rcP!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b924285-cb9b-4c7f-b38e-1fe72d930e37_4400x2880.png 1272w, /__u/substackcdn.com/image/fetch/$s_!4rcP!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5b924285-cb9b-4c7f-b38e-1fe72d930e37_4400x2880.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>3. Load Score Composition</h3><p>A single scalar load score is more useful than reacting to individual metrics in isolation. CPU alone is a poor signal &#8212; a GC pause shows as 100% CPU, but the right response (reduce batch) is identical to high processing latency from an unrelated cause. Compositing metrics makes the controller robust to noisy individual signals:</p><pre><code><code>loadScore = (cpuLoad            &#215; 0.40)
          + (heapUsageRatio     &#215; 0.30)
          + (normalizedLag      &#215; 0.20)
          + (normalizedProcTime &#215; 0.10)
</code></code></pre><p><strong>Weight rationale</strong>: CPU and heap are the primary resource constraints for batch processing (0.70 combined). Consumer lag drives urgency &#8212; when lag grows, the system is falling behind regardless of local resource state (0.20). Processing time per record measures throughput efficiency directly (0.10).</p><p><strong>Key architectural insight</strong>: Consumer lag is a <em>leading indicator</em> of downstream SLA breach. When lag accelerates, reducing batch size counter-intuitively helps: smaller batches complete faster, allowing the broker fetch loop to execute more iterations per second, catching up with lower per-iteration memory pressure.</p><h3>4. Adjustment Strategy and the Dead Zone</h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;1615566d-1b3d-479d-bfa5-071e3fbc70d8&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">if loadScore &gt; 0.75:   # High load &#8212; shrink batch
    target = currentBatch &#215; 0.80

elif loadScore &lt; 0.40 and processingMs &lt; TARGET_LATENCY_MS:
    target = currentBatch &#215; 1.20  # only grow if latency budget allows

else:
    target = currentBatch          # dead zone &#8212; hold steady

# Exponential smoothing
newBatch = (int)(currentBatch &#215; 0.70 + target &#215; 0.30)
newBatch = clamp(newBatch, MIN_BATCH, MAX_BATCH)</code></pre></div><p>The <strong>dead zone</strong> (0.40&#8211;0.75 load) is intentional and critical. Without it, the controller hunts continuously between grow and shrink on every evaluation cycle. In production, stability within an acceptable band is more valuable than chasing the theoretical optimum every 10 seconds.</p><h3>5. Spring Kafka Integration &#8212; The Mechanical Challenge</h3><p>Kafka&#8217;s <code>ConcurrentKafkaListenerContainerFactory</code> creates <code>KafkaMessageListenerContainer</code> instances at startup. Adjusting <code>max.poll.records</code> dynamically requires reaching into the container via the <code>KafkaListenerEndpointRegistry</code>, which Spring exposes as a managed bean.</p><p>The critical constraint: the new batch size <strong>takes effect on the next poll cycle</strong>, not mid-batch. This is correct behavior &#8212; you never want batch size changing mid-processing. The controller evaluates every 10 seconds via <code>@Scheduled</code>, which means batch size adjustments flow through on the next poll after the scheduled evaluation fires.</p><p><strong>Trade-off</strong>: Running the controller at 1-second intervals would be more responsive but adds overhead on every evaluation cycle. At 10 seconds, the system can absorb a 10-second load spike before responding. Size this interval against your SLA recovery time objective.</p><div><hr></div><h3>Github Link :</h3><p><a href="https://github.com/sysdr/sdc-java-p/tree/main/day72/day72-adaptive-batching">https://github.com/sysdr/sdc-java-p/tree/main/day72/day72-adaptive-batching</a></p><div><hr></div><h2>Implementation Walkthrough</h2><h3>LoadMonitorService &#8212; Reading System State</h3><p>The service collects four signals. The expensive operation &#8212; AdminClient consumer lag query &#8212; is TTL-cached at 5 seconds to avoid hammering the broker on every load sample:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;f125a952-5def-4cb1-a407-9adc68bb218d&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">@Service
public class LoadMonitorService {
    private volatile CachedLag lagCache = new CachedLag(0L, Instant.EPOCH);

    public SystemLoadSnapshot getCurrentLoad() {
        double cpuLoad   = osMXBean.getCpuLoad();
        double heapRatio = getHeapUsageRatio();
        long   lag       = getCachedConsumerLag(); // TTL=5s

        return SystemLoadSnapshot.builder()
            .cpuLoad(cpuLoad)
            .heapUsageRatio(heapRatio)
            .consumerLag(lag)
            .timestamp(Instant.now())
            .build();
    }
}
</code></pre></div><p><strong>Failure mode</strong>: when <code>AdminClient.listConsumerGroupOffsets()</code> fails, the service returns a conservative high-lag sentinel, biasing the controller toward smaller batches. Always fail toward lower throughput, never toward OOM.</p><h3>AdaptiveBatchController &#8212; The Control Loop</h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;915c6536-126a-4107-baf1-f15986836081&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">@Service
public class AdaptiveBatchController {
    private static final int    MIN_BATCH = 10;
    private static final int    MAX_BATCH = 500;
    private static final double HIGH_LOAD = 0.75;
    private static final double LOW_LOAD  = 0.40;

    private final AtomicInteger currentBatchSize = new AtomicInteger(100);

    @Scheduled(fixedDelay = 10_000)
    public void evaluate() {
        SystemLoadSnapshot snap = loadMonitor.getCurrentLoad();
        double score = computeLoadScore(snap);
        int    next  = applyControlLaw(score, snap.getLastProcessingMs());
        currentBatchSize.set(next);
        emitMetrics(score, next); // Micrometer gauge: log.consumer.batch.size.current
    }

    private int applyControlLaw(double score, long processingMs) {
        double raw = currentBatchSize.get();
        if (score &gt; HIGH_LOAD)                             raw *= 0.80;
        else if (score &lt; LOW_LOAD &amp;&amp; processingMs &lt; 200L) raw *= 1.20;
        int smoothed = (int)(currentBatchSize.get() * 0.70 + raw * 0.30);
        return Math.clamp(smoothed, MIN_BATCH, MAX_BATCH);
    }
}
</code></pre></div><h3>AdaptiveLogConsumer &#8212; Closing the Loop</h3><p>Each listener batch records its actual size and wall-clock processing time, feeding back into <code>LoadMonitorService</code>:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;864828a0-1898-4537-9a97-3de8144f9f56&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">@KafkaListener(topics = "${kafka.topic.log-events}",
               containerFactory = "adaptiveBatchListenerContainerFactory")
public void consume(List&lt;ConsumerRecord&lt;String, String&gt;&gt; records, Consumer&lt;?, ?&gt; consumer) {
    long start    = System.currentTimeMillis();
    int  batchUsed = records.size();

    processBatch(records);

    long elapsed = System.currentTimeMillis() - start;
    // This single line closes the feedback loop
    loadMonitor.recordBatchCompletion(new BatchMetrics(batchUsed, elapsed, ...));
}
</code></pre></div><p>The <code>AdaptiveBatchConfig</code> wires a <code>ContainerCustomizer</code> that updates <code>max.poll.records</code> on the running container before each poll, pulling the current value from <code>AdaptiveBatchController.getCurrentBatchSize()</code>.</p><div><hr></div><h2>Production Considerations</h2><p><strong>GC pressure and batch sizing</strong>: Large batches under memory pressure trigger G1GC concurrent marking, which appears as CPU spikes and feeds back into the load score as &#8220;high load&#8221; &#8212; correctly triggering batch reduction. This is the control loop working as intended.</p><p><strong>Kafka broker alignment</strong>: <code>MAX_BATCH_SIZE &#215; avg_record_size</code> must not exceed <code>max.partition.fetch.bytes</code> (default 1MB). At 500 records &#215; 2KB average = 1MB exactly &#8212; validate this alignment or the broker silently truncates fetches, making your effective ceiling lower than configured.</p><p><strong>What to alert on</strong>: Alert on <code>currentBatchSize == MIN_BATCH</code> sustained for &gt;5 minutes &#8212; the system is overwhelmed and adaptive tuning has hit its floor. Alert on batch size oscillation (high variance over a 2-minute window) &#8212; indicates the smoothing factor is too low or the dead zone is too narrow.</p><p><strong>Observability</strong>: The Micrometer gauge <code>log.consumer.batch.size.current</code> becomes the most informative single metric in your dashboard. Watch it alongside <code>kafka.consumer.fetch-manager.records-lag-max</code> and <code>jvm.gc.pause.total</code> &#8212; the correlation between GC pressure and batch size reduction should be visible within one controller evaluation cycle.</p><div><hr></div><h2>Implementation Guide : </h2><p>This guide covers how to build, run, observe, and verify the Day 72 adaptive batching system. All commands assume you are in the project root directory (the folder containing <code>docker-compose.yml</code>, <code>start.sh</code>, and <code>pom.xml</code>).</p><h3>Prerequisites</h3><p>Tool Version Purpose Java JDK 17+ (21 recommended) Build Spring Boot modules Maven 3.8+ Package JARs Docker 24+ Run Kafka, Postgres, Redis, Prometheus, Grafana, apps Docker Compose v2+ Orchestrate the stack curl any Health checks and API calls python3 3.8+ Integration test helper scripts</p><p>Ensure these ports are free before starting: <strong>8081</strong>, <strong>8082</strong>, <strong>3000</strong>, <strong>9091</strong>, <strong>5432</strong>, <strong>6379</strong>, <strong>9092</strong>, <strong>2181</strong>.</p><div><hr></div><h3>Way 1 &#8212; One-command startup (recommended)</h3><pre><code><code>chmod +x start.sh stop.sh cleanup.sh load-test.sh integration-tests/test-adaptive-batching.sh
./start.sh
</code></code></pre><p><code>start.sh</code> will:</p><ol><li><p>Build all Maven modules (<code>mvn clean package -DskipTests</code>)</p></li><li><p>Start the Docker Compose stack with a fresh build</p></li><li><p>Wait until producer, consumer, Prometheus, and Grafana are healthy</p></li><li><p>Seed demo log events so dashboard metrics are non-zero</p></li></ol><div><hr></div><h3>Way 2 &#8212; Manual step-by-step</h3><pre><code><code># 1. Build JARs
mvn clean package -DskipTests

# 2. Start infrastructure and apps
docker compose up -d --build

# 3. Wait ~60&#8211;90 seconds, then verify health
curl http://localhost:8081/actuator/health
curl http://localhost:8082/actuator/health
curl http://localhost:9091/-/ready
curl http://localhost:3000/api/health

# 4. Send a test event
curl -X POST http://localhost:8081/api/logs \
  -H "Content-Type: application/json" \
  -d '{"service":"demo","level":"INFO","message":"hello","environment":"test"}'

# 5. Run integration tests
./integration-tests/test-adaptive-batching.sh

# 6. Run a load demo
./load-test.sh light    # batch size should grow
./load-test.sh spike    # light &#8594; heavy &#8594; recovery (best for Grafana)
</code></code></pre><div><hr></div><h3>Way 3 &#8212; Run apps locally (without Docker for Java services)</h3><p>Use this when you want to debug Java code directly. Infrastructure still runs in Docker.</p><pre><code><code># Start only infrastructure
docker compose up -d zookeeper kafka kafka-init postgres redis prometheus grafana

# Terminal 1 &#8212; producer
cd log-producer
KAFKA_BOOTSTRAP_SERVERS=localhost:9092 mvn spring-boot:run

# Terminal 2 &#8212; consumer
cd log-consumer
KAFKA_BOOTSTRAP_SERVERS=localhost:9092 \
POSTGRES_HOST=localhost REDIS_HOST=localhost \
mvn spring-boot:run
</code></code></pre><div><hr></div><h3>Way 4 &#8212; Run unit tests only</h3><pre><code><code>mvn clean test</code></code></pre><p>Tests cover <code>SystemLoadSnapshot</code> scoring and <code>LogEventController</code> request handling.</p><div><hr></div><h3>Stopping and cleaning up</h3><p>Script What it does <code>./stop.sh</code> Stops all project containers; <strong>keeps</strong> Docker volumes (data persists) <code>./cleanup.sh</code> Stops stack, removes project volumes/images, prunes dangling Docker resources <code>./cleanup.sh --full</code> Same as above plus <code>docker system prune -a --volumes</code> (removes all unused images/volumes)</p><pre><code><code>./stop.sh
# or full cleanup before git commit / disk reclaim:
./cleanup.sh
</code></code></pre><div><hr></div><h3>Viewing output</h3><h4>Service health</h4><pre><code><code>curl http://localhost:8081/actuator/health   # Producer
curl http://localhost:8082/actuator/health   # Consumer
</code></code></pre><h4>Raw Prometheus metrics</h4><pre><code><code># Consumer adaptive batching metrics
curl -s http://localhost:8082/actuator/prometheus | grep log_consumer

# Producer throughput
curl -s http://localhost:8081/actuator/prometheus | grep log_producer
</code></code></pre><h4>Prometheus UI</h4><p>Open </p><p>http://localhost:9091</p><p> and query:</p><ul><li><p><code>log_consumer_batch_size_current</code></p></li><li><p><code>log_consumer_load_score</code></p></li><li><p><code>rate(log_consumer_records_processed_total[1m])</code></p></li><li><p><code>rate(log_producer_messages_sent_total{status="success"}[1m])</code></p></li></ul><h4>Grafana dashboard (primary observability)</h4><p>Item Value URL <a href="http://localhost:3000/d/day72-adaptive-batch">http://localhost:3000/d/day72-adaptive-batch</a> Login <code>admin</code> / <code>admin</code> (local demo credentials) Refresh 5 s (live)</p><p>Dashboard panels:</p><ol><li><p><strong>Batch Size &#8212; Self-Tuning</strong> &#8212; <code>log_consumer_batch_size_current</code> (should move between 10&#8211;500)</p></li><li><p><strong>Load Score [0&#8211;1]</strong> &#8212; <code>log_consumer_load_score</code></p></li><li><p><strong>Records Processed / sec</strong> &#8212; consumer throughput rate</p></li><li><p><strong>Avg Batch Processing Time (ms)</strong> &#8212; per-batch latency</p></li><li><p><strong>JVM Heap Usage %</strong> &#8212; consumer heap pressure</p></li><li><p><strong>Producer Send Rate</strong> &#8212; events published per second</p></li></ol><h4>Application logs</h4><pre><code><code>docker compose logs -f log-consumer
docker compose logs -f log-producer
docker compose logs -f kafka
</code></code></pre><div><hr></div><h3>Working Code Demo:</h3><div id="youtube2-6a6e-_ln-nA" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;6a6e-_ln-nA&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/6a6e-_ln-nA?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><div><hr></div><h3>Success criteria</h3><p>Use this checklist to confirm the system is working correctly.</p><h4>Startup</h4><ul><li><p><code>curl http://localhost:8081/actuator/health</code> returns <code>{"status":"UP"}</code></p></li><li><p><code>curl http://localhost:8082/actuator/health</code> returns <code>{"status":"UP"}</code></p></li><li><p><code>./integration-tests/test-adaptive-batching.sh</code> exits 0 with all checks passed</p></li><li><p>Exactly one container per service (<code>docker ps --filter name=day72-adaptive-batching</code>)</p></li></ul><h4>Data flow</h4><ul><li><p><code>POST /api/logs</code> returns <code>202</code> with <code>"status":"queued"</code></p></li><li><p><code>POST /api/logs/batch</code> returns <code>"queued": N</code> matching batch size</p></li><li><p><code>log_consumer_records_processed_total</code> increases after sending events</p></li><li><p><code>log_producer_messages_sent_total{status="success"}</code> increases</p></li></ul><h4>Adaptive batching behavior</h4><ul><li><p><code>log_consumer_batch_size_current</code> is <strong>not stuck at 0</strong> (initial value ~100)</p></li><li><p>Under <strong>light load</strong> (<code>./load-test.sh light</code>), batch size <strong>grows</strong> toward max (500)</p></li><li><p>Under <strong>heavy load</strong> (<code>./load-test.sh heavy</code>), batch size <strong>shrinks</strong> toward min (10)</p></li><li><p><code>log_consumer_load_score</code> stays in range <strong>0.0 &#8211; 1.0</strong> and responds to load changes</p></li><li><p>Batch size changes appear in Grafana within one evaluation interval (~10 s)</p></li></ul><h4>Dashboard (non-zero, live values)</h4><ul><li><p>Batch Size panel shows a line above zero</p></li><li><p>Load Score gauge shows a value between 0 and 1</p></li><li><p>Records/sec panel shows activity during <code>./load-test.sh</code></p></li><li><p>Producer Send Rate panel shows activity during load test</p></li><li><p>JVM Heap % shows a percentage (typically 15&#8211;40 % under load)</p></li><li><p>Panels auto-refresh every 5 s without manual reload</p></li></ul><h4>Example passing metric snapshot</h4><p>After <code>./load-test.sh light</code> for 30+ seconds:</p><pre><code><code>log_consumer_batch_size_current     &gt; 100
log_consumer_load_score             between 0.0 and 1.0
log_consumer_records_processed_total &gt; 0
rate(log_consumer_records_processed_total[1m]) &gt; 0
rate(log_producer_messages_sent_total{status="success"}[1m]) &gt; 0</code></code></pre><div><hr></div><h3>Troubleshooting</h3><p>Symptom Likely cause Fix Port already in use Previous stack still running <code>./cleanup.sh</code> then <code>./start.sh</code> Producer returns 500 Kafka not ready or Snappy/native lib issue Wait 60 s; rebuild with <code>./start.sh</code> Grafana panels empty No traffic yet or Prometheus scrape delay Run <code>./load-test.sh light</code>; wait 20 s Duplicate containers Stack started twice <code>./cleanup.sh</code> then <code>./start.sh</code> Prometheus on 9090 conflict Host Prometheus already bound This project uses host port <strong>9091</strong></p><div><hr></div><h3>Configuration reference</h3><p>Key tuning parameters live in <code>log-consumer/src/main/resources/application.yml</code> under <code>adaptive.batch</code>:</p><p>Parameter Default Effect <code>evaluation-interval-ms</code> 10000 Control loop frequency <code>high-load-threshold</code> 0.75 Shrink batch when score exceeds this <code>low-load-threshold</code> 0.40 Grow batch when score below this <code>min-size</code> 10 Minimum <code>max.poll.records</code> <code>max-size</code> 500 Maximum <code>max.poll.records</code> <code>target-latency-ms</code> 200 Only grow if batch processing is faster than this</p><div><hr></div><h3>Quick reference</h3><pre><code><code>./start.sh                                          # start everything
./integration-tests/test-adaptive-batching.sh       # verify end-to-end
./load-test.sh spike                                # demo adaptive behavior
open http://localhost:3000/d/day72-adaptive-batch   # Grafana dashboard
./stop.sh                                           # stop (keep data)
./cleanup.sh                                        # stop + prune Docker</code></code></pre><div><hr></div><h2>Scale Connection</h2><p>Confluent Cloud dynamically tunes <code>fetch.min.bytes</code> and consumer throughput quotas based on broker-observed partition load &#8212; the same feedback principle at infrastructure scale. LinkedIn&#8217;s original Kafka design paper (2011) identified consumer batch sizing as the primary lever for throughput/latency trade-offs. Uber&#8217;s real-time data platform team published in 2019 that their consumer fleet auto-scales batch sizes across time zones as daily load patterns shift. What you are building today is a software-layer implementation of what cloud Kafka platforms implement at the broker layer.</p><div><hr></div><h2>Next Steps</h2><p>Day 73 adds Redis caching layers so query-heavy log analytics stop hammering PostgreSQL on every dashboard refresh.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://sdcourse.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Hands On System Design - Distributed Systems Implementation  is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Week 12: Advanced Analytics — “Turn raw logs into decisions”]]></title><description><![CDATA[What we&#8217;re building today]]></description><link>https://sdcourse.substack.com/p/week-12-advanced-analytics-turn-raw</link><guid isPermaLink="false">https://sdcourse.substack.com/p/week-12-advanced-analytics-turn-raw</guid><dc:creator><![CDATA[System Design Course]]></dc:creator><pubDate>Sat, 15 Aug 2026 06:30:24 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!LnPy!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7a13e96-22cc-435c-bbeb-549e166014e7_6000x4100.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3><strong>What we&#8217;re building today</strong></h3><blockquote><p>When Datadog surfaces an outage, engineers don&#8217;t read millions of lines&#8212;they rely on classification, clustering, correlation, forecasting, root-cause graphs, and fix recommendations working as one pipeline. Splunk&#8217;s ML Toolkit and Elastic&#8217;s Observability AI follow the same pattern: enrich first, then reason across signals. This lesson wires <strong>seven analytics layers</strong> into one platform.</p></blockquote><p><strong>By the end you&#8217;ll have:</strong></p><ul><li><p>NLP enrichment extracting intent, entities, and sentiment from free-text logs</p></li><li><p>ML classification predicting severity and category</p></li><li><p>Clustering discovering patterns and flagging anomalies</p></li><li><p>Cross-source correlation linking related events</p></li><li><p>Ensemble forecasting with alert levels on response-time metrics</p></li><li><p>Root-cause analysis with causal graphs on incidents</p></li><li><p>Troubleshooting recommendations from similar past incidents</p></li></ul><blockquote><p>This project <strong>integrates</strong> analytics patterns from prior lessons without modifying those source directories.</p></blockquote><div><hr></div><h2><strong>Why this matters</strong></h2><ul><li><p><strong>PagerDuty AIOps</strong> groups related alerts before paging&#8212;your correlation engine does this at log-ingest scale.</p></li><li><p><strong>Google Borgmon / Monarch</strong> forecast capacity from time-series&#8212;your ensemble forecaster mirrors that on <code>response_time</code>.</p></li><li><p><strong>Elastic ML</strong> clusters rare log templates&#8212;your KMeans + DBSCAN pipeline surfaces the same anomaly signals.</p></li><li><p><strong>New Relic incident intelligence</strong> ranks probable root causes&#8212;your RCA engine uses causal ordering and confidence scoring.</p></li><li><p><strong>GitHub Copilot for SRE</strong> suggests fixes from similar tickets&#8212;your recommendation engine uses TF-IDF similarity, not black-box magic.</p></li></ul><blockquote><p>Analytics is not one model&#8212;it is a staged pipeline where each layer adds context the next layer needs.</p></blockquote><div><hr></div><h2><strong>Core concepts</strong></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!dm5x!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F189c00e8-ffa1-450f-a95d-8bcc19a79688_872x667.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!dm5x!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F189c00e8-ffa1-450f-a95d-8bcc19a79688_872x667.png 424w, /__u/substackcdn.com/image/fetch/$s_!dm5x!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F189c00e8-ffa1-450f-a95d-8bcc19a79688_872x667.png 848w, /__u/substackcdn.com/image/fetch/$s_!dm5x!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F189c00e8-ffa1-450f-a95d-8bcc19a79688_872x667.png 1272w, /__u/substackcdn.com/image/fetch/$s_!dm5x!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F189c00e8-ffa1-450f-a95d-8bcc19a79688_872x667.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!dm5x!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F189c00e8-ffa1-450f-a95d-8bcc19a79688_872x667.png" width="872" height="667" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/189c00e8-ffa1-450f-a95d-8bcc19a79688_872x667.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:667,&quot;width&quot;:872,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:127742,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://sdcourse.substack.com/i/200727825?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F189c00e8-ffa1-450f-a95d-8bcc19a79688_872x667.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!dm5x!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F189c00e8-ffa1-450f-a95d-8bcc19a79688_872x667.png 424w, /__u/substackcdn.com/image/fetch/$s_!dm5x!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F189c00e8-ffa1-450f-a95d-8bcc19a79688_872x667.png 848w, /__u/substackcdn.com/image/fetch/$s_!dm5x!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F189c00e8-ffa1-450f-a95d-8bcc19a79688_872x667.png 1272w, /__u/substackcdn.com/image/fetch/$s_!dm5x!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F189c00e8-ffa1-450f-a95d-8bcc19a79688_872x667.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><div><hr></div><h2><strong>Architecture</strong></h2><blockquote><p>Single <strong>FastAPI</strong> service on port <strong>8095</strong>. All analytics modules run in-process&#8212;same coordination logic as a distributed production stack without extra containers for learning.</p></blockquote><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!LnPy!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7a13e96-22cc-435c-bbeb-549e166014e7_6000x4100.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!LnPy!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7a13e96-22cc-435c-bbeb-549e166014e7_6000x4100.png 424w, /__u/substackcdn.com/image/fetch/$s_!LnPy!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7a13e96-22cc-435c-bbeb-549e166014e7_6000x4100.png 848w, /__u/substackcdn.com/image/fetch/$s_!LnPy!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7a13e96-22cc-435c-bbeb-549e166014e7_6000x4100.png 1272w, /__u/substackcdn.com/image/fetch/$s_!LnPy!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7a13e96-22cc-435c-bbeb-549e166014e7_6000x4100.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!LnPy!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7a13e96-22cc-435c-bbeb-549e166014e7_6000x4100.png" width="1456" height="995" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e7a13e96-22cc-435c-bbeb-549e166014e7_6000x4100.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:995,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1236413,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://sdcourse.substack.com/i/200727825?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7a13e96-22cc-435c-bbeb-549e166014e7_6000x4100.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!LnPy!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7a13e96-22cc-435c-bbeb-549e166014e7_6000x4100.png 424w, /__u/substackcdn.com/image/fetch/$s_!LnPy!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7a13e96-22cc-435c-bbeb-549e166014e7_6000x4100.png 848w, /__u/substackcdn.com/image/fetch/$s_!LnPy!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7a13e96-22cc-435c-bbeb-549e166014e7_6000x4100.png 1272w, /__u/substackcdn.com/image/fetch/$s_!LnPy!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe7a13e96-22cc-435c-bbeb-549e166014e7_6000x4100.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="callout-block" data-callout="true"><p>Preparing for a distributed systems interview?</p><p>&#8594;<a href="https://systemdrd.com/ebooks/sdcourse-distributed-systems-interview">Download the free Interview Pack</a></p><p>&#8594; <a href="/__u/sdcourse.substack.com/subscribe">Subscribe</a> now to access source code repository - 200 + coding lessons</p></div>
      <p>
          <a href="/__u/sdcourse.substack.com/p/week-12-advanced-analytics-turn-raw">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Day 177: Edge Log Collection for Limited Connectivity]]></title><description><![CDATA[Week 26: IoT and Edge Log Processing | 254-Day Hands-On System Design Series]]></description><link>https://sdcourse.substack.com/p/day-177-edge-log-collection-for-limited</link><guid isPermaLink="false">https://sdcourse.substack.com/p/day-177-edge-log-collection-for-limited</guid><dc:creator><![CDATA[System Design Course]]></dc:creator><pubDate>Thu, 13 Aug 2026 08:26:17 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!N16Y!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd57e6048-2281-49d5-973c-1f8ba1790a8f_3440x2240.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>What We&#8217;re Building Today</h2><ul><li><p>Local SQLite log buffer that persists logs during network outages</p></li><li><p>Connectivity monitor that detects online/offline transitions in real time</p></li><li><p>Store-and-forward sync engine with priority-based flushing</p></li><li><p>Central FastAPI receiver with deduplication and sequence reassembly</p></li><li><p>React dashboard showing edge device sync status and buffer depth</p></li></ul><div><hr></div><h2>The Problem Nobody Talks About</h2><p>Most logging tutorials assume a stable network. Edge devices don&#8217;t get that luxury.</p><p>An Amazon Flex driver&#8217;s handheld scanner logs 400 package-scan events before hitting a dead zone between warehouses. A wind turbine sensor records vibration anomalies for 6 hours before the maintenance truck&#8217;s cellular modem comes back in range. A hospital medication cart logs 200 dispense events in an elevator with zero signal.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://sdcourse.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Hands On System Design - Distributed Systems Implementation  is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>In all three cases, <strong>the logs still need to arrive at the central system&#8212;complete, ordered, and deduplicated</strong>.</p><p>This is the store-and-forward problem, and it&#8217;s fundamentally different from what Days 31&#8211;36 covered with RabbitMQ. There, the broker was always reachable. Here, the broker might not exist from the device&#8217;s perspective for hours.</p><div><hr></div><h2>Core Architecture</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!N16Y!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd57e6048-2281-49d5-973c-1f8ba1790a8f_3440x2240.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!N16Y!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd57e6048-2281-49d5-973c-1f8ba1790a8f_3440x2240.png 424w, /__u/substackcdn.com/image/fetch/$s_!N16Y!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd57e6048-2281-49d5-973c-1f8ba1790a8f_3440x2240.png 848w, /__u/substackcdn.com/image/fetch/$s_!N16Y!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd57e6048-2281-49d5-973c-1f8ba1790a8f_3440x2240.png 1272w, /__u/substackcdn.com/image/fetch/$s_!N16Y!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd57e6048-2281-49d5-973c-1f8ba1790a8f_3440x2240.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!N16Y!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd57e6048-2281-49d5-973c-1f8ba1790a8f_3440x2240.png" width="1456" height="948" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d57e6048-2281-49d5-973c-1f8ba1790a8f_3440x2240.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:948,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:813958,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://sdcourse.substack.com/i/206405677?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd57e6048-2281-49d5-973c-1f8ba1790a8f_3440x2240.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!N16Y!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd57e6048-2281-49d5-973c-1f8ba1790a8f_3440x2240.png 424w, /__u/substackcdn.com/image/fetch/$s_!N16Y!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd57e6048-2281-49d5-973c-1f8ba1790a8f_3440x2240.png 848w, /__u/substackcdn.com/image/fetch/$s_!N16Y!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd57e6048-2281-49d5-973c-1f8ba1790a8f_3440x2240.png 1272w, /__u/substackcdn.com/image/fetch/$s_!N16Y!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd57e6048-2281-49d5-973c-1f8ba1790a8f_3440x2240.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Three distinct zones interact in this system:</p><p><strong>Edge Zone</strong> &#8212; The device itself. A lightweight Python agent runs continuously, writing incoming log events to a local SQLite buffer regardless of network state. Each log gets a monotonically increasing local sequence number and a device-scoped UUID.</p><p><strong>Sync Engine</strong> &#8212; A background thread watches the connectivity monitor. The moment the network returns, it reads unsynced logs from the buffer ordered by priority tier (ERROR &gt; WARN &gt; INFO &gt; DEBUG), batches them into compressed payloads, and ships them upstream. On acknowledgment, it marks records as synced.</p><p><strong>Central Receiver</strong> &#8212; A FastAPI endpoint accepts batches from any number of edge devices. It runs a deduplication check using the device ID + sequence number composite key, merges out-of-order arrivals using the embedded timestamp, and commits clean records to the central log store.</p><div><hr></div><h2>How Connectivity Awareness Works</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!BomX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c4f7987-d34c-4346-8dbd-6ffeeec5592d_3120x2400.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!BomX!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c4f7987-d34c-4346-8dbd-6ffeeec5592d_3120x2400.png 424w, /__u/substackcdn.com/image/fetch/$s_!BomX!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c4f7987-d34c-4346-8dbd-6ffeeec5592d_3120x2400.png 848w, /__u/substackcdn.com/image/fetch/$s_!BomX!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c4f7987-d34c-4346-8dbd-6ffeeec5592d_3120x2400.png 1272w, /__u/substackcdn.com/image/fetch/$s_!BomX!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c4f7987-d34c-4346-8dbd-6ffeeec5592d_3120x2400.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!BomX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c4f7987-d34c-4346-8dbd-6ffeeec5592d_3120x2400.png" width="1456" height="1120" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4c4f7987-d34c-4346-8dbd-6ffeeec5592d_3120x2400.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1120,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:729579,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://sdcourse.substack.com/i/206405677?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c4f7987-d34c-4346-8dbd-6ffeeec5592d_3120x2400.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!BomX!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c4f7987-d34c-4346-8dbd-6ffeeec5592d_3120x2400.png 424w, /__u/substackcdn.com/image/fetch/$s_!BomX!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c4f7987-d34c-4346-8dbd-6ffeeec5592d_3120x2400.png 848w, /__u/substackcdn.com/image/fetch/$s_!BomX!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c4f7987-d34c-4346-8dbd-6ffeeec5592d_3120x2400.png 1272w, /__u/substackcdn.com/image/fetch/$s_!BomX!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c4f7987-d34c-4346-8dbd-6ffeeec5592d_3120x2400.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The connectivity monitor doesn&#8217;t use a ping to 8.8.8.8 &#8212; that&#8217;s brittle. Instead it attempts a lightweight HTTP HEAD request to the central receiver&#8217;s <code>/health</code> endpoint. This confirms both network reachability <em>and</em> that the receiver is operational.</p><p>State transitions matter here:</p><pre><code><code>OFFLINE &#8594; ONLINE   : trigger immediate sync of entire buffer
ONLINE  &#8594; OFFLINE  : switch to local-only write mode, stop sync attempts
ONLINE  &#8594; ONLINE   : periodic sync tick (every 30 seconds)
</code></code></pre><p>The edge agent never drops logs in any state. The buffer is the source of truth on the device.</p><div><hr></div><h2>SQLite as the Edge Buffer</h2><p>Why SQLite and not a file or in-memory queue? Three reasons:</p><ol><li><p><strong>Crash safety</strong> &#8212; WAL mode gives you atomic writes. If the device powers off mid-write, no corruption.</p></li><li><p><strong>Priority queries</strong> &#8212; <code>SELECT * FROM logs WHERE synced=0 ORDER BY priority DESC, seq ASC LIMIT 100</code> is ten characters of SQL, not 200 lines of custom heap code.</p></li><li><p><strong>Deduplication</strong> &#8212; The <code>device_id + seq</code> unique constraint prevents double-writes on the device side.</p></li></ol><p>Schema is deliberately minimal:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;98650b44-2261-4fb7-92d1-9120d6550502&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">CREATE TABLE edge_logs (
    seq       INTEGER PRIMARY KEY AUTOINCREMENT,
    device_id TEXT NOT NULL,
    ts        REAL NOT NULL,
    level     TEXT NOT NULL,
    payload   TEXT NOT NULL,
    synced    INTEGER DEFAULT 0,
    priority  INTEGER DEFAULT 3
);</code></pre></div><div><hr></div><h2>Sequence Reassembly at the Receiver</h2><p>When three devices each send batches simultaneously after coming back online, the receiver sees interleaved sequence numbers. Tesla&#8217;s vehicle telemetry team solved this exact pattern: each vehicle is its own ordering namespace. You never sort across devices&#8212;only within a device&#8217;s own sequence stream.</p><p>The receiver maintains a per-device <code>last_received_seq</code> watermark. Gaps in the sequence trigger a 10-second wait window before committing, allowing retransmissions to fill holes. After the window, any still-missing sequences get flagged as &#8220;gap detected&#8221; in the central log metadata.</p><div><hr></div><h2>What You&#8217;ll See Running</h2><p>Terminal shows the edge agent cycling through states &#8212; writing logs locally, detecting network loss, buffering silently, reconnecting, then flushing the backlog in priority order. The React dashboard shows each simulated device as a card: buffer depth, sync percentage, last seen timestamp, and connectivity state badge (green/red).</p><p>The central server logs show batch arrivals with deduplication counts &#8212; confirming that even if a device sends the same batch twice (network retry), records appear exactly once in the central store.</p><div><hr></div><h2>Integration with the Bigger System</h2><p>This edge collector slots in directly below Week 25&#8217;s executive dashboard. The C-level views built on Day 176 now gain a new data source: edge devices that were previously invisible during connectivity gaps. The sync engine&#8217;s <code>synced_at</code> timestamp lets the dashboard distinguish real-time data from backfilled historical data &#8212; a distinction that matters enormously in operations analytics.</p><p>Day 178 builds on today&#8217;s sync engine by adding compression and delta encoding to the batch payloads, reducing the bytes transferred per log by up to 80%.</p><h3>Github Link:</h3><p><a href="https://github.com/sysdr/course-p/tree/main/day177/day177_edge_log_collector">https://github.com/sysdr/course-p/tree/main/day177/day177_edge_log_collector</a></p><h1>Implementation Guide Step-to-Step</h1><p>Local <strong>edge log buffer</strong> with <strong>store-and-forward sync</strong> to a <strong>central FastAPI receiver</strong>, plus a <strong>React (Vite)</strong> dashboard served from the API when <code>frontend/dist</code> is built.</p><p>There are <strong>no API keys</strong> in this demo; run it only on trusted networks or behind your own auth/TLS.</p><div><hr></div><h2>Requirements</h2><ul><li><p>Python <strong>3.11+</strong> recommended (3.12 works with the pinned deps in <code>requirements.txt</code>)</p></li><li><p><strong>Node.js 20+</strong> and npm (only if you build or develop the dashboard)</p></li><li><p><strong>Docker</strong> and Docker Compose plugin (optional, for container run)</p></li></ul><p>Install Python dependencies:</p><pre><code><code>python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt</code></code></pre><div><hr></div><h2>Build the dashboard</h2><pre><code><code>cd frontend
npm install
npm run build
cd ..</code></code></pre><p>The API serves <code>/</code> from <code>frontend/dist/index.html</code> and bundles under <code>/assets/</code> after a successful build.</p><p>Development server (alternate):</p><pre><code><code>cd frontend &amp;&amp; npm run dev</code></code></pre><p>Uses </p><p>http://localhost:5173</p><p> and calls the API at </p><p>http://127.0.0.1:8100</p><p>.</p><div><hr></div><h2>Run (local)</h2><p>From this directory:</p><pre><code><code>./start.sh</code></code></pre><ul><li><p>API + dashboard: </p></li></ul><p>http://127.0.0.1:8100/</p><ul><li><p>OpenAPI: <strong><a href="http://127.0.0.1:8100/docs">http://127.0.0.1:8100/docs</a></strong></p></li></ul><p>Stop:</p><pre><code><code>./stop.sh</code></code></pre><div><hr></div><h2>Tests</h2><pre><code><code>source venv/bin/activate
pytest tests/ -v --tb=short</code></code></pre><div><hr></div><h2>Docker</h2><pre><code><code>cd docker
docker compose up --build</code></code></pre><p>Compose builds from the <strong>parent</strong> directory (<code>context: ..</code> in <code>docker-compose.yml</code>). Runtime SQLite and logs use the mounted <code>../logs</code> volume.</p><p>Tear down and free unused Docker disk:</p><pre><code><code>./cleanup.sh</code></code></pre><div><hr></div><h2>Cleanup script</h2><p><code>cleanup.sh</code> runs <code>./stop.sh</code>, <code>docker compose down</code> for this project (when Docker is available), then <code>docker container|network|image|builder prune</code> to drop unused objects. Review flags before running on shared machines; it does <strong>not</strong> run <code>docker system prune -a</code> (no aggressive removal of all unused images).</p><h2>Working Code Demo:</h2><div id="youtube2-SdlUALe0MP8" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;SdlUALe0MP8&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/SdlUALe0MP8?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><div><hr></div><h2>Success Criteria</h2><p>By the end of this build you&#8217;ll have:</p><ul><li><p>Edge agent that writes logs to local SQLite with zero data loss during simulated network outages</p></li><li><p>Sync engine that detects connectivity and flushes the buffer in priority order</p></li><li><p>Central receiver that deduplicates and merges batches from multiple devices</p></li><li><p>Dashboard showing per-device buffer depth and sync status in real time</p></li></ul><div><hr></div><h2>Assignment</h2><p>Extend the edge agent with a <strong>configurable retention policy</strong>: logs older than N hours that haven&#8217;t synced should be promoted to priority 1 (highest) and, if still unsynced after an additional M hours, written to a local compressed archive file and removed from the SQLite buffer to prevent unbounded disk growth.</p><p><strong>Solution hints</strong>: Add a <code>created_at</code> column to the schema. Run a background task every 15 minutes that queries <code>WHERE synced=0 AND created_at &lt; (now - N_hours)</code> and updates their priority. A second pass queries <code>WHERE synced=0 AND created_at &lt; (now - N+M_hours)</code>, serializes them to a <code>.jsonl.gz</code> archive using Python&#8217;s <code>gzip</code> module, then deletes those rows.</p><div><hr></div><p><em>Next: Day 178 &#8212; Making log transport bandwidth-efficient with delta encoding and adaptive compression for constrained networks</em></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://sdcourse.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Hands On System Design - Distributed Systems Implementation  is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Day 71: Profile and Optimize the Log Ingestion Pipeline]]></title><description><![CDATA[What We&#8217;re Building Today]]></description><link>https://sdcourse.substack.com/p/day-71-profile-and-optimize-the-log</link><guid isPermaLink="false">https://sdcourse.substack.com/p/day-71-profile-and-optimize-the-log</guid><dc:creator><![CDATA[System Design Course]]></dc:creator><pubDate>Tue, 11 Aug 2026 08:30:50 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!M8Uf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc74ddae-e636-4b95-ba8a-a58521674a1a_2720x2960.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>What We&#8217;re Building Today</h2><ul><li><p><strong>JVM profiling harness</strong> using async-profiler + JFR to find CPU/memory hotspots in the ingestion path</p></li><li><p><strong>Batching and compression tuning</strong> across the Kafka producer, consumer, and HTTP ingest layer</p></li><li><p><strong>Thread-pool right-sizing</strong> with measured queue saturation and latency percentiles</p></li><li><p><strong>Before/after benchmark dashboard</strong> &#8212; Prometheus metrics exposed so the improvement is quantifiable, not anecdotal</p></li></ul><div><hr></div><h2>Why This Matters</h2><blockquote><p>Every distributed log pipeline eventually hits a performance wall. The wall isn&#8217;t random &#8212; it&#8217;s almost always the same three culprits: serialization overhead eating CPU, undersized thread pools causing artificial queuing, and na&#239;ve per-event I/O that could be batched. Netflix&#8217;s Vector team traced 40 % of their ingestion CPU to Jackson serialization before switching to a binary codec. Uber&#8217;s M3 pipeline saw 3&#215; throughput gains purely from Kafka producer batch tuning without touching business logic.</p><p>The insight that separates senior engineers from mid-level ones: <strong>you don&#8217;t optimize what you guess &#8212; you optimize what the profiler proves.</strong> Premature optimization is a maintenance tax with no guaranteed dividend. Today we instrument first, measure under realistic load, identify the actual bottleneck, and then apply a targeted fix. Then we measure again. That loop &#8212; profile &#8594; hypothesis &#8594; change &#8594; validate &#8212; is the discipline that makes improvements stick.</p></blockquote><div><hr></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!M8Uf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc74ddae-e636-4b95-ba8a-a58521674a1a_2720x2960.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!M8Uf!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc74ddae-e636-4b95-ba8a-a58521674a1a_2720x2960.png 424w, /__u/substackcdn.com/image/fetch/$s_!M8Uf!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc74ddae-e636-4b95-ba8a-a58521674a1a_2720x2960.png 848w, /__u/substackcdn.com/image/fetch/$s_!M8Uf!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc74ddae-e636-4b95-ba8a-a58521674a1a_2720x2960.png 1272w, /__u/substackcdn.com/image/fetch/$s_!M8Uf!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc74ddae-e636-4b95-ba8a-a58521674a1a_2720x2960.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!M8Uf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc74ddae-e636-4b95-ba8a-a58521674a1a_2720x2960.png" width="1456" height="1584" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cc74ddae-e636-4b95-ba8a-a58521674a1a_2720x2960.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1584,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:794210,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://sdcourse.substack.com/i/207246124?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc74ddae-e636-4b95-ba8a-a58521674a1a_2720x2960.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!M8Uf!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc74ddae-e636-4b95-ba8a-a58521674a1a_2720x2960.png 424w, /__u/substackcdn.com/image/fetch/$s_!M8Uf!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc74ddae-e636-4b95-ba8a-a58521674a1a_2720x2960.png 848w, /__u/substackcdn.com/image/fetch/$s_!M8Uf!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc74ddae-e636-4b95-ba8a-a58521674a1a_2720x2960.png 1272w, /__u/substackcdn.com/image/fetch/$s_!M8Uf!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcc74ddae-e636-4b95-ba8a-a58521674a1a_2720x2960.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>System Design Deep Dive</h2><h3>1. The Ingestion Pipeline as a Series of Queues</h3><p>Think of the pipeline as linked queues: HTTP thread pool &#8594; deserialization &#8594; Kafka producer internal batch buffer &#8594; Kafka broker &#8594; consumer thread pool &#8594; persistence write pool. <strong>Little&#8217;s Law</strong> governs every stage: throughput = concurrency / latency. When one stage saturates, back-pressure ripples upstream. Profiling tells you <em>which</em> queue is the bottleneck.</p><p><strong>Anti-pattern:</strong> tuning all stages equally. This wastes engineering time and often creates new bottlenecks by overpowering downstream stages.</p>
      <p>
          <a href="/__u/sdcourse.substack.com/p/day-71-profile-and-optimize-the-log">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Week 5: Message Queues for Log Processing]]></title><description><![CDATA[When Log Processing Becomes a Messaging Problem]]></description><link>https://sdcourse.substack.com/p/week-5-message-queues-for-log-processing</link><guid isPermaLink="false">https://sdcourse.substack.com/p/week-5-message-queues-for-log-processing</guid><dc:creator><![CDATA[System Design Course]]></dc:creator><pubDate>Sat, 08 Aug 2026 06:30:48 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!k4S9!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15262f60-ab9d-4280-9401-4384cdcf8a3f_3920x2480.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1>When Log Processing Becomes a Messaging Problem</h1><p>Production backends rarely process logs synchronously inside the HTTP request that generated them. Under load, tying observability to the critical path increases tail latency, hides failures behind timeouts, and makes it impossible to absorb traffic spikes without dropping data. Message queues decouple <strong>ingestion</strong> from <strong>processing</strong>&#8212;and this Spring Boot application shows how that decoupling composes into a deployable system.</p><h2>What We Built</h2><ul><li><p>A single Spring Boot 3.2 application (<code>Week5IntegratedApplication</code>) hosting seven composable messaging modules: RabbitMQ topic routing, Kafka producer semantics, consumer groups, manual acknowledgment with idempotency, content-based fan-out, dead-letter classification, and priority-tier processing</p></li><li><p>Profile-driven infrastructure toggles (<code>app.kafka.enabled</code>, <code>app.rabbitmq.enabled</code>, <code>app.redis.enabled</code>) so the same JAR runs on a laptop with in-memory brokers or against a full Docker stack</p></li><li><p>Resilience4j circuit breakers on Kafka producers and log processing, Spring Retry on transient failures, and SQL-backed DLQ records that survive broker outages</p></li><li><p>An operations dashboard at </p></li></ul><p>http://localhost:8080/</p><ul><li><p> aggregating Micrometer counters, module metrics, and a one-click demo that exercises every pipeline</p></li></ul><h2>Why This Matters</h2><blockquote><p>At Netflix, Uber, or Datadog scale, log pipelines ingest billions of events daily. No team writes directly to Elasticsearch from a servlet thread. Producers publish durable events; brokers buffer them; consumers work at a sustainable rate with explicit reliability contracts&#8212;acknowledgments, retries, dead-letter routing, and priority tiers. This project implements those contracts in one JVM so you can trace how they interact rather than studying each pattern in isolation.</p></blockquote><h2>System Design Deep Dive</h2><p><strong>Topic exchange routing decouples producers from topology.</strong> The RabbitMQ module publishes <code>Day31LogEvent</code> instances through <code>RabbitTemplate</code> when brokers are enabled, or through <code>Day31InMemoryBrokerService</code> locally&#8212;simulating <code>logs-critical</code>, <code>logs-processing</code>, and <code>logs-monitoring</code> queues with routing keys that encode severity and category. Producers never hard-code consumer topology; bindings decide destination&#8212;a direct analogue to production systems that hide queue names behind exchange configuration.</p><p><strong>Kafka producer tuning exposes reliability trade-offs.</strong> <code>Day32KafkaProducerService</code> offers sync, async, and fire-and-forget send paths guarded by a Resilience4j circuit breaker. Producer config sets <code>acks=all</code> and idempotent sends via <code>Week5KafkaConfig</code>. A custom <code>Day32LogPartitioner</code> routes high-severity events to dedicated partitions&#8212;an operational concern when you need ordered processing per tenant without starving the rest of the topic.</p><p><strong>At-least-once delivery requires business-layer idempotency.</strong> <code>Day34LogProcessingService</code> combines Spring Retry with hybrid deduplication via <code>Day34IdempotencyKeyRepository</code> and optional Redis <code>SETNX</code>. Committing an offset or ACK only tells the broker you took responsibility once; deduplication is what makes retries safe.</p><p><strong>DLQ design needs taxonomy, not a single bucket.</strong> <code>Day36DeadLetterQueueService</code> classifies failures as transient (retry topic), permanent (DLQ), or ambiguous (retry then DLQ), always persisting to <code>Day36DlqRecordRepository</code> before optional Kafka mirroring&#8212;so operators query failure state even when the cluster is down.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!k4S9!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15262f60-ab9d-4280-9401-4384cdcf8a3f_3920x2480.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!k4S9!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15262f60-ab9d-4280-9401-4384cdcf8a3f_3920x2480.png 424w, /__u/substackcdn.com/image/fetch/$s_!k4S9!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15262f60-ab9d-4280-9401-4384cdcf8a3f_3920x2480.png 848w, /__u/substackcdn.com/image/fetch/$s_!k4S9!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15262f60-ab9d-4280-9401-4384cdcf8a3f_3920x2480.png 1272w, /__u/substackcdn.com/image/fetch/$s_!k4S9!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15262f60-ab9d-4280-9401-4384cdcf8a3f_3920x2480.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!k4S9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15262f60-ab9d-4280-9401-4384cdcf8a3f_3920x2480.png" width="1456" height="921" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/15262f60-ab9d-4280-9401-4384cdcf8a3f_3920x2480.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:921,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1521532,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://sdcourse.substack.com/i/206547409?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15262f60-ab9d-4280-9401-4384cdcf8a3f_3920x2480.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!k4S9!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15262f60-ab9d-4280-9401-4384cdcf8a3f_3920x2480.png 424w, /__u/substackcdn.com/image/fetch/$s_!k4S9!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15262f60-ab9d-4280-9401-4384cdcf8a3f_3920x2480.png 848w, /__u/substackcdn.com/image/fetch/$s_!k4S9!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15262f60-ab9d-4280-9401-4384cdcf8a3f_3920x2480.png 1272w, /__u/substackcdn.com/image/fetch/$s_!k4S9!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F15262f60-ab9d-4280-9401-4384cdcf8a3f_3920x2480.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div>
      <p>
          <a href="/__u/sdcourse.substack.com/p/week-5-message-queues-for-log-processing">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Day 176: Build Executive Dashboards for Business Metrics]]></title><description><![CDATA[Week 25: Business Analytics Use Cases | 254-Day Hands-On System Design Series Module: Business Intelligence from Log Processing Systems]]></description><link>https://sdcourse.substack.com/p/day-176-build-executive-dashboards</link><guid isPermaLink="false">https://sdcourse.substack.com/p/day-176-build-executive-dashboards</guid><dc:creator><![CDATA[System Design Course]]></dc:creator><pubDate>Thu, 06 Aug 2026 08:30:31 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!dqDL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e240bb8-caf1-4c82-a36b-467523e937e3_3440x2080.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>What We&#8217;re Building Today</h2><ul><li><p>A C-level executive dashboard that pulls <strong>business KPIs</strong> from your distributed log processing pipeline</p></li><li><p>Real-time <strong>revenue impact scoring</strong>, <strong>SLA compliance views</strong>, and <strong>system health indexes</strong> surfaced as board-ready metrics</p></li><li><p>Role-segmented views: one backend aggregation engine, three audience lenses (CEO, CTO, CFO)</p></li><li><p>React frontend with auto-refreshing metric cards and trend sparklines &#8212; no charting library needed</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!dqDL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e240bb8-caf1-4c82-a36b-467523e937e3_3440x2080.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!dqDL!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e240bb8-caf1-4c82-a36b-467523e937e3_3440x2080.png 424w, /__u/substackcdn.com/image/fetch/$s_!dqDL!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e240bb8-caf1-4c82-a36b-467523e937e3_3440x2080.png 848w, /__u/substackcdn.com/image/fetch/$s_!dqDL!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e240bb8-caf1-4c82-a36b-467523e937e3_3440x2080.png 1272w, /__u/substackcdn.com/image/fetch/$s_!dqDL!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e240bb8-caf1-4c82-a36b-467523e937e3_3440x2080.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!dqDL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e240bb8-caf1-4c82-a36b-467523e937e3_3440x2080.png" width="1456" height="880" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9e240bb8-caf1-4c82-a36b-467523e937e3_3440x2080.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:880,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:952698,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://sdcourse.substack.com/i/206403750?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e240bb8-caf1-4c82-a36b-467523e937e3_3440x2080.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!dqDL!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e240bb8-caf1-4c82-a36b-467523e937e3_3440x2080.png 424w, /__u/substackcdn.com/image/fetch/$s_!dqDL!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e240bb8-caf1-4c82-a36b-467523e937e3_3440x2080.png 848w, /__u/substackcdn.com/image/fetch/$s_!dqDL!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e240bb8-caf1-4c82-a36b-467523e937e3_3440x2080.png 1272w, /__u/substackcdn.com/image/fetch/$s_!dqDL!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e240bb8-caf1-4c82-a36b-467523e937e3_3440x2080.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h2>The Hidden Problem Nobody Talks About</h2><p>Your distributed log system already knows which requests timed out, which payment flows failed, and which services are degrading. The tragedy is that this intelligence stays locked in Grafana dashboards that only on-call engineers open at 2 AM.</p><p>Executives make resource allocation decisions &#8212; hiring, infrastructure spend, roadmap priority &#8212; with almost no visibility into what the logs already know. Salesforce famously surfaced this problem internally when they discovered their support ticket volume spiked every Monday morning <em>because</em> their weekend batch jobs were silently corrupting user records. The signal was in the logs; it just never reached anyone who could act on it.</p><p>Today you close that gap.</p><div><hr></div><h2>Core Concept: Log-to-KPI Translation</h2><p>Raw logs aren&#8217;t business metrics. The translation layer is what makes this lesson non-trivial.</p><p><strong>What logs contain:</strong><br>HTTP status codes, response times, user IDs, endpoint names, service identifiers, error payloads.</p><p><strong>What executives need:</strong><br>Availability percentage, revenue-at-risk from degraded services, customer-impacting incident count, SLA breach rate, mean-time-to-recovery.</p><p>The translation pipeline works in three stages:</p><ol><li><p><strong>Log Classifier</strong> &#8212; tags each log event with a business domain (<code>payment</code>, <code>auth</code>, <code>checkout</code>, <code>search</code>) and impact type (<code>revenue-critical</code>, <code>ux-degrading</code>, <code>infrastructure</code>)</p></li><li><p><strong>KPI Aggregator</strong> &#8212; rolls up classified events into 1-minute, 15-minute, and 1-hour windows; computes percentages, counts, and weighted impact scores</p></li><li><p><strong>Dashboard Renderer</strong> &#8212; serves role-specific views that filter and frame the same aggregated data for CEO (outcome), CTO (system), and CFO (cost/risk) audiences</p></li></ol><div><hr></div><h2>Architecture</h2><p>The system extends your Day 175 customer experience monitoring output. The log stream feeds a FastAPI aggregation service that maintains rolling window metrics in memory (using Python&#8217;s <code>collections.deque</code>). A React SPA polls the API every 15 seconds and renders three role-specific panels.</p><p><strong>Key design choice:</strong> No time-series database. Rolling windows held in memory keep the implementation self-contained and fast for a single-node demo &#8212; exactly the pattern DoorDash used for their internal ops dashboard before they scaled it to Prometheus.</p>
      <p>
          <a href="/__u/sdcourse.substack.com/p/day-176-build-executive-dashboards">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Day 70: Compliance Report Generation and Export Pipeline]]></title><description><![CDATA[What We&#8217;re Building Today]]></description><link>https://sdcourse.substack.com/p/day-70-compliance-report-generation</link><guid isPermaLink="false">https://sdcourse.substack.com/p/day-70-compliance-report-generation</guid><dc:creator><![CDATA[System Design Course]]></dc:creator><pubDate>Tue, 04 Aug 2026 08:30:24 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Zcm9!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00e0ff6c-a606-430b-82c2-cf7453ca72c0_2720x1832.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>What We&#8217;re Building Today</h2><ul><li><p><strong>Audit Event Consumer</strong>: Kafka consumer that ingests and deduplicates compliance-relevant events into a dedicated audit store</p></li><li><p><strong>Multi-Format Export Engine</strong>: Strategy-pattern exporters producing PDF, CSV, and JSON reports for regulatory consumption</p></li><li><p><strong>Scheduled Report Generator</strong>: Cron-driven GDPR, SOC2, and PCI-DSS report automation with async generation and status tracking</p></li><li><p><strong>Compliance Report API</strong>: REST endpoints for on-demand report generation, status polling, and authenticated file download</p></li></ul><div><hr></div><h2>Why This Matters</h2><p>Every company handling user data operates under compliance frameworks&#8212;GDPR, SOC2, PCI-DSS, HIPAA. When an auditor arrives (or a regulator sends a letter), the difference between a 3-hour response and a 3-week scramble is whether your compliance data is pre-collected and queryable or scattered across operational databases.</p><p>The naive approach&#8212;running ad-hoc SQL queries against your production database when audit season arrives&#8212;fails at scale. Uber processes 50M+ trips daily; their operational database can&#8217;t absorb analytical compliance queries. Netflix handles thousands of GDPR data-access requests monthly via dedicated compliance infrastructure. Stripe&#8217;s audit system is completely isolated from its payment processing path.</p><p>The pattern: <strong>stream audit events into a compliance-specific read model continuously, then generate reports asynchronously from that model</strong>. This decouples compliance data collection from report generation, and both from your operational systems.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!Zcm9!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00e0ff6c-a606-430b-82c2-cf7453ca72c0_2720x1832.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!Zcm9!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00e0ff6c-a606-430b-82c2-cf7453ca72c0_2720x1832.png 424w, /__u/substackcdn.com/image/fetch/$s_!Zcm9!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00e0ff6c-a606-430b-82c2-cf7453ca72c0_2720x1832.png 848w, /__u/substackcdn.com/image/fetch/$s_!Zcm9!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00e0ff6c-a606-430b-82c2-cf7453ca72c0_2720x1832.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Zcm9!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00e0ff6c-a606-430b-82c2-cf7453ca72c0_2720x1832.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!Zcm9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00e0ff6c-a606-430b-82c2-cf7453ca72c0_2720x1832.png" width="1456" height="981" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/00e0ff6c-a606-430b-82c2-cf7453ca72c0_2720x1832.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:981,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:320415,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://sdcourse.substack.com/i/207115383?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00e0ff6c-a606-430b-82c2-cf7453ca72c0_2720x1832.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!Zcm9!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00e0ff6c-a606-430b-82c2-cf7453ca72c0_2720x1832.png 424w, /__u/substackcdn.com/image/fetch/$s_!Zcm9!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00e0ff6c-a606-430b-82c2-cf7453ca72c0_2720x1832.png 848w, /__u/substackcdn.com/image/fetch/$s_!Zcm9!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00e0ff6c-a606-430b-82c2-cf7453ca72c0_2720x1832.png 1272w, /__u/substackcdn.com/image/fetch/$s_!Zcm9!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F00e0ff6c-a606-430b-82c2-cf7453ca72c0_2720x1832.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div><h2>System Design Deep Dive</h2><h3>1. Event Sourcing as Your Audit Foundation</h3><p>Compliance reporting requires an <strong>immutable, append-only audit trail</strong>. Every user data access, deletion, authentication event, and admin action becomes an event with a unique ID, actor, timestamp, and outcome.</p><p>The critical property: <strong>audit events must be immutable after creation</strong>. This is non-negotiable for regulatory validity. Your audit store should never support <code>UPDATE</code> on event records&#8212;only <code>INSERT</code> and <code>SELECT</code>. Compliance frameworks treat mutable audit logs as evidence of tampering.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:&quot;a5d606b2-d3d2-4a2b-a79f-b94bca62b579&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">AuditEvent {
    id: UUID (Kafka message key - used for deduplication)
    userId, action, resourceType, resourceId
    eventTime, ipAddress, outcome, metadata
    serviceOrigin  // which microservice emitted this
}</code></pre></div><p><strong>Trade-off</strong>: Event sourcing means your compliance store grows indefinitely. Plan for partitioned tables by month and archival to cold storage after 7 years (the SOC2/GDPR retention floor).</p><h3>2. CQRS: Separate Your Compliance Read Model</h3>
      <p>
          <a href="/__u/sdcourse.substack.com/p/day-70-compliance-report-generation">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Week 11: Performance Optimization — Make every log byte earn its keep]]></title><description><![CDATA[What we&#8217;re building today]]></description><link>https://sdcourse.substack.com/p/week-11-performance-optimization</link><guid isPermaLink="false">https://sdcourse.substack.com/p/week-11-performance-optimization</guid><dc:creator><![CDATA[System Design Course]]></dc:creator><pubDate>Sat, 01 Aug 2026 06:30:21 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!HUXS!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F265ac9c8-dc88-4995-b267-ba315a503b62_5500x3200.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2><strong>What we&#8217;re building today</strong></h2><blockquote><p>When Cloudflare ingests 10M+ HTTP logs per second, they don&#8217;t write one event at a time&#8212;they batch, compress, and skip duplicates before touching disk. Datadog caches hot queries in memory tiers. ClickHouse picks columnar storage because analytics scans columns, not rows. This lesson wires <strong>seven performance layers</strong> into one platform.</p></blockquote><p><strong>By the end you&#8217;ll have:</strong></p><ul><li><p>Pipeline profiler with bottleneck detection</p></li><li><p>Self-tuning adaptive batching</p></li><li><p>L1 LRU query cache</p></li><li><p>Row / columnar / hybrid storage selection</p></li><li><p>Bloom filter deduplication</p></li><li><p>Delta encoding with zlib compression</p></li><li><p>Adaptive worker scaling</p></li></ul><div><hr></div><h2><strong>Why this matters</strong></h2><ul><li><p><strong>Kafka producers</strong> batch messages because each network round-trip costs milliseconds&#8212;your adaptive batcher learns the sweet spot automatically.</p></li><li><p><strong>Redis</strong> sits in front of Postgres for hot keys&#8212;your L1 cache does the same for repeated log queries.</p></li><li><p><strong>S3 lifecycle rules</strong> move cold data to cheaper tiers&#8212;your storage optimizer picks row vs columnar based on access patterns.</p></li><li><p><strong>Cassandra</strong> uses bloom filters to skip SSTable reads&#8212;your bloom layer skips duplicate log fingerprints at ingest.</p></li><li><p><strong>InfluxDB</strong> delta-encodes timestamps&#8212;your encoder stores only what changed since the last baseline.</p></li></ul><blockquote><p>Performance is not one trick&#8212;it is a stack of small decisions that compound.</p></blockquote><div><hr></div><h2><strong>Core concepts</strong></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!_2xl!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0432337-ce34-42fc-a344-4596ac74fb2a_867x602.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!_2xl!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0432337-ce34-42fc-a344-4596ac74fb2a_867x602.png 424w, /__u/substackcdn.com/image/fetch/$s_!_2xl!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0432337-ce34-42fc-a344-4596ac74fb2a_867x602.png 848w, /__u/substackcdn.com/image/fetch/$s_!_2xl!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0432337-ce34-42fc-a344-4596ac74fb2a_867x602.png 1272w, /__u/substackcdn.com/image/fetch/$s_!_2xl!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0432337-ce34-42fc-a344-4596ac74fb2a_867x602.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!_2xl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0432337-ce34-42fc-a344-4596ac74fb2a_867x602.png" width="867" height="602" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a0432337-ce34-42fc-a344-4596ac74fb2a_867x602.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:602,&quot;width&quot;:867,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:95080,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://sdcourse.substack.com/i/200727729?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0432337-ce34-42fc-a344-4596ac74fb2a_867x602.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!_2xl!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0432337-ce34-42fc-a344-4596ac74fb2a_867x602.png 424w, /__u/substackcdn.com/image/fetch/$s_!_2xl!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0432337-ce34-42fc-a344-4596ac74fb2a_867x602.png 848w, /__u/substackcdn.com/image/fetch/$s_!_2xl!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0432337-ce34-42fc-a344-4596ac74fb2a_867x602.png 1272w, /__u/substackcdn.com/image/fetch/$s_!_2xl!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0432337-ce34-42fc-a344-4596ac74fb2a_867x602.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><strong>Architecture</strong></h2><blockquote><p>Single <strong>FastAPI</strong> service on port <strong>8095</strong>. All performance modules run in-process with SQLite persistence.</p></blockquote><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!7Twp!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F884e947f-685f-494c-89bd-398dfccaedfa_5900x3600.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!7Twp!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F884e947f-685f-494c-89bd-398dfccaedfa_5900x3600.png 424w, /__u/substackcdn.com/image/fetch/$s_!7Twp!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F884e947f-685f-494c-89bd-398dfccaedfa_5900x3600.png 848w, /__u/substackcdn.com/image/fetch/$s_!7Twp!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F884e947f-685f-494c-89bd-398dfccaedfa_5900x3600.png 1272w, /__u/substackcdn.com/image/fetch/$s_!7Twp!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F884e947f-685f-494c-89bd-398dfccaedfa_5900x3600.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!7Twp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F884e947f-685f-494c-89bd-398dfccaedfa_5900x3600.png" width="1456" height="888" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/884e947f-685f-494c-89bd-398dfccaedfa_5900x3600.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:888,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:900240,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://sdcourse.substack.com/i/200727729?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F884e947f-685f-494c-89bd-398dfccaedfa_5900x3600.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!7Twp!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F884e947f-685f-494c-89bd-398dfccaedfa_5900x3600.png 424w, /__u/substackcdn.com/image/fetch/$s_!7Twp!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F884e947f-685f-494c-89bd-398dfccaedfa_5900x3600.png 848w, /__u/substackcdn.com/image/fetch/$s_!7Twp!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F884e947f-685f-494c-89bd-398dfccaedfa_5900x3600.png 1272w, /__u/substackcdn.com/image/fetch/$s_!7Twp!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F884e947f-685f-494c-89bd-398dfccaedfa_5900x3600.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="callout-block" data-callout="true"><p>Preparing for a distributed systems interview?</p><p>&#8594;<a href="https://systemdrd.com/ebooks/sdcourse-distributed-systems-interview">Download the free Interview Pack</a></p><p>&#8594; <a href="/__u/sdcourse.substack.com/subscribe">Subscribe</a> now to access source code repository - 200 + coding lessons</p></div>
      <p>
          <a href="/__u/sdcourse.substack.com/p/week-11-performance-optimization">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Day 175: Customer Experience Monitoring — Turning Logs into User Stories]]></title><description><![CDATA[Week 25: Business Analytics Use Cases | From the &#8220;254-Day Hands-On System Design&#8221; Series]]></description><link>https://sdcourse.substack.com/p/day-175-customer-experience-monitoring</link><guid isPermaLink="false">https://sdcourse.substack.com/p/day-175-customer-experience-monitoring</guid><dc:creator><![CDATA[System Design Course]]></dc:creator><pubDate>Thu, 30 Jul 2026 08:31:32 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!A5Sn!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3577de1-db08-48cb-9219-8ae48008e28e_3280x2160.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Yesterday, we built an A/B test analysis framework that compares experiment cohorts statistically. Today we shift perspective entirely &#8212; instead of comparing groups, we watch <strong>individual user journeys</strong> unfold through log data and surface the friction points that silently drive customers away.</p><div><hr></div><h2>What We&#8217;re Building Today</h2><ul><li><p>Parse raw application logs into structured user sessions</p></li><li><p>Compute CX metrics: page load time, error encounter rate, session abandonment, funnel completion</p></li><li><p>Build a Python 3.11 FastAPI backend that aggregates these metrics in real-time</p></li><li><p>React dashboard showing live CX health at a glance</p></li><li><p>Integrate cleanly into the log pipeline built across prior weeks</p></li></ul><div><hr></div><h2>Why This Problem Is Hard</h2><p>When Shopify&#8217;s checkout page added 300ms of latency during Black Friday 2021, it cost merchants millions before anyone noticed &#8212; because no single error fired. The signal was buried in the <strong>aggregate pattern</strong> of user sessions quietly abandoning mid-funnel. Standard error-rate monitoring saw nothing. Customer experience monitoring would have caught it in minutes.</p><p>Logs tell you what <em>happened</em>. CX monitoring tells you what the user <em>experienced</em>.</p><div><hr></div><h2>Core Concepts</h2><p><strong>Session Stitching</strong> &#8212; Log lines arrive with user IDs and timestamps but no session boundaries. We detect sessions by grouping events per user with a 30-minute inactivity timeout. This mirrors how Google Analytics and Amplitude define sessions.</p><p><strong>Funnel State Machine</strong> &#8212; Every product has a conversion funnel (e.g., <code>landing &#8594; product_view &#8594; cart &#8594; checkout &#8594; purchase</code>). We model this as a state machine where each log event advances or exits a session&#8217;s state. Abandoned sessions are those that entered but never exited through purchase.</p><p><strong>Percentile Latency, Not Averages</strong> &#8212; Average page load time hides the 5% of users on slow connections who see 8-second loads. We compute p50, p90, p95, p99 &#8212; the same metrics Stripe and Cloudflare publish in their status pages.</p><p><strong>Error Encounter Rate</strong> &#8212; The fraction of sessions that hit at least one 5xx or client-reported JS error. A healthy system keeps this under 0.5%. Amplitude&#8217;s engineering team found that users who encounter even one error have 3&#215; the churn rate.</p><div><hr></div><h2>Architecture</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!A5Sn!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3577de1-db08-48cb-9219-8ae48008e28e_3280x2160.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!A5Sn!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3577de1-db08-48cb-9219-8ae48008e28e_3280x2160.png 424w, /__u/substackcdn.com/image/fetch/$s_!A5Sn!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3577de1-db08-48cb-9219-8ae48008e28e_3280x2160.png 848w, /__u/substackcdn.com/image/fetch/$s_!A5Sn!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3577de1-db08-48cb-9219-8ae48008e28e_3280x2160.png 1272w, /__u/substackcdn.com/image/fetch/$s_!A5Sn!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3577de1-db08-48cb-9219-8ae48008e28e_3280x2160.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!A5Sn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3577de1-db08-48cb-9219-8ae48008e28e_3280x2160.png" width="1456" height="959" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a3577de1-db08-48cb-9219-8ae48008e28e_3280x2160.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:959,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:617768,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://sdcourse.substack.com/i/206402093?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3577de1-db08-48cb-9219-8ae48008e28e_3280x2160.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!A5Sn!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3577de1-db08-48cb-9219-8ae48008e28e_3280x2160.png 424w, /__u/substackcdn.com/image/fetch/$s_!A5Sn!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3577de1-db08-48cb-9219-8ae48008e28e_3280x2160.png 848w, /__u/substackcdn.com/image/fetch/$s_!A5Sn!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3577de1-db08-48cb-9219-8ae48008e28e_3280x2160.png 1272w, /__u/substackcdn.com/image/fetch/$s_!A5Sn!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3577de1-db08-48cb-9219-8ae48008e28e_3280x2160.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The system has three layers:</p><p><strong>Ingestion Layer</strong> &#8212; A <code>LogIngestor</code> reads structured JSON logs from your existing pipeline (the Kafka/Redis stream from Week 5). Each log line carries: <code>user_id</code>, <code>session_id</code>, <code>event_type</code>, <code>page</code>, <code>latency_ms</code>, <code>status_code</code>, <code>timestamp</code>.</p><p><strong>Computation Layer</strong> &#8212; A <code>SessionAggregator</code> maintains in-memory session windows per user. When a session closes (timeout or purchase), it emits a <code>SessionSummary</code> object. A <code>MetricsComputer</code> consumes summaries and updates rolling percentile buckets using a T-Digest algorithm &#8212; O(1) memory regardless of data volume.</p><p><strong>Serving Layer</strong> &#8212; FastAPI exposes <code>/metrics/cx</code> returning the current CX health snapshot. React polls every 10 seconds and renders a dashboard with trend sparklines.</p><div><hr></div><h2>Data Flow</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!EjvW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4234fdd9-1c83-471d-9777-1797b38276e3_3200x2080.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!EjvW!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4234fdd9-1c83-471d-9777-1797b38276e3_3200x2080.png 424w, /__u/substackcdn.com/image/fetch/$s_!EjvW!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4234fdd9-1c83-471d-9777-1797b38276e3_3200x2080.png 848w, /__u/substackcdn.com/image/fetch/$s_!EjvW!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4234fdd9-1c83-471d-9777-1797b38276e3_3200x2080.png 1272w, /__u/substackcdn.com/image/fetch/$s_!EjvW!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4234fdd9-1c83-471d-9777-1797b38276e3_3200x2080.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!EjvW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4234fdd9-1c83-471d-9777-1797b38276e3_3200x2080.png" width="1456" height="946" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4234fdd9-1c83-471d-9777-1797b38276e3_3200x2080.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:946,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:630428,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://sdcourse.substack.com/i/206402093?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4234fdd9-1c83-471d-9777-1797b38276e3_3200x2080.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!EjvW!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4234fdd9-1c83-471d-9777-1797b38276e3_3200x2080.png 424w, /__u/substackcdn.com/image/fetch/$s_!EjvW!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4234fdd9-1c83-471d-9777-1797b38276e3_3200x2080.png 848w, /__u/substackcdn.com/image/fetch/$s_!EjvW!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4234fdd9-1c83-471d-9777-1797b38276e3_3200x2080.png 1272w, /__u/substackcdn.com/image/fetch/$s_!EjvW!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4234fdd9-1c83-471d-9777-1797b38276e3_3200x2080.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><pre><code><code>Raw Log Line
    &#8594; SessionAggregator (stitch by user_id + 30-min gap)
        &#8594; SessionSummary (funnel_stage, errors_seen, load_times[], completed)
            &#8594; MetricsComputer (rolling p50/p95/p99, error_rate, abandonment_rate)
                &#8594; Redis (5-min sliding window snapshots)
                    &#8594; FastAPI &#8594; React Dashboard</code></code></pre><p>State transitions per session: <code>STARTED &#8594; BROWSING &#8594; INTENT (cart) &#8594; CHECKOUT &#8594; CONVERTED</code> or <code>&#8594; ABANDONED</code> at any stage.</p><div><hr></div><h2>Key Implementation Pieces</h2><p><strong>Session Aggregator</strong> &#8212; Keyed on <code>user_id</code>, emits session on 30-min idle or explicit <code>session_end</code> event. Uses a min-heap of expiry times to efficiently close stale sessions without scanning all active ones.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;e50d4401-f73e-4a2f-b733-eecef8d8fede&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python"># Pseudo-code
class SessionAggregator:
    def ingest(self, log_line: dict) -&gt; SessionSummary | None:
        session = self.active[log_line['user_id']]
        session.update(log_line)
        if session.is_expired() or log_line['event'] == 'session_end':
            return self.close(session)
</code></pre></div><p><strong>T-Digest Percentile Tracker</strong> &#8212; A lightweight implementation of the T-Digest algorithm gives accurate percentile estimates with ~1KB of memory per metric. No need for heavy libraries.</p><p><strong>Funnel Abandonment Detection</strong> &#8212; Each session carries its highest funnel stage reached. At close time, sessions that reached <code>INTENT</code> or <code>CHECKOUT</code> but not <code>CONVERTED</code> are flagged as high-value abandonments &#8212; the metric product teams care most about.</p><div><hr></div><h2>What the Dashboard Shows</h2><p>Four metric cards: <strong>p95 Page Load</strong>, <strong>Error Encounter Rate</strong>, <strong>Funnel Completion Rate</strong>, <strong>Abandonment by Stage</strong>. A time-series sparkline for each updates every 10 seconds. Color thresholds turn cards amber/red when metrics breach SLO bounds &#8212; the same pattern Datadog uses for its SLO widgets.</p><div><hr></div><h2>Real-World Grounding</h2><p>Amazon&#8217;s CX team famously measured that 100ms of added latency reduced revenue by 1%. Their monitoring pipeline is exactly this: session stitching from distributed logs &#8594; funnel analysis &#8594; real-time dashboards &#8212; all derived from logs, not from invasive client-side tracking that users block.</p><div><hr></div><h2>Integration with Week 25</h2><p>This module consumes the same enriched log stream the A/B framework (Day 174) reads from. Tomorrow (Day 176), your executive dashboard will pull directly from the <code>/metrics/cx</code> endpoint you build today &#8212; giving C-level stakeholders a one-pager built on the same underlying data.</p><div><hr></div><h3>Github Link:</h3><p><a href="https://github.com/sysdr/course-p/tree/main/day175/day175_cx_monitoring">https://github.com/sysdr/course-p/tree/main/day175/day175_cx_monitoring</a></p><h2>Implementation Guide Step-to-Step</h2><h2><strong>Prerequisites</strong></h2><ul><li><p><strong>Python</strong> 3.10+ (<code>python3</code> or <code>python3.11</code>).</p></li><li><p><code>pip</code> inside a virtual environment (recommended).</p></li></ul><p>Optional:</p><ul><li><p><strong>Docker</strong> and Docker Compose Plugin for containerized runs.</p></li></ul><div><hr></div><h2><strong>Install and run (local)</strong></h2><p>From this directory (<code>day175_cx_monitoring/</code>):</p><pre><code><code>python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt</code></code></pre><p>Start the API and dashboard:</p><pre><code><code>uvicorn src.api.server:app --host 0.0.0.0 --port 8175</code></code></pre><p>Or use helpers (creates venv if needed, installs deps, runs tests, starts server):</p><pre><code><code>chmod +x start.sh stop.sh cleanup.sh   # once
./start.sh
</code></code></pre><p>Stop:</p><pre><code><code>./stop.sh
</code></code></pre><p><strong>URLs:</strong></p><ul><li><p>Dashboard and API root: </p></li></ul><p>http://localhost:8175/</p><ul><li><p>Metrics JSON: <strong><a href="http://localhost:8175/metrics/cx">http://localhost:8175/metrics/cx</a></strong></p></li><li><p>Health: <strong><a href="http://localhost:8175/health">http://localhost:8175/health</a></strong></p></li></ul><div><hr></div><h2><strong>Tests</strong></h2><pre><code><code>source .venv/bin/activate
python -m pytest tests/ -v --tb=short
</code></code></pre><p>Dependencies for tests (<code>pytest</code>, <code>pytest-asyncio</code>, <code>httpx</code>) are listed in <code>requirements.txt</code>.</p><div><hr></div><h2><strong>Docker</strong></h2><p>From <code>docker/</code> (Compose build context is the project root):</p><pre><code><code>cd docker
docker compose up --build</code></code></pre><p>The service listens on host port <strong>8175</strong> mapped to container port <strong>8175</strong>.</p><div><hr></div><h2><strong>Tear down and Docker cleanup</strong></h2><pre><code><code>./cleanup.sh</code></code></pre><p>This stops the local HTTP service, brings down this project&#8217;s Compose stack (removes <strong>local</strong> images built for <code>cx-monitor</code>), and runs non-destructive <code>docker ... prune -f</code> cleanups.</p><p>To also remove <strong>all</strong> unused Docker images globally (including non-dangling):</p><pre><code><code>DOCKER_PRUNE_ALL_UNUSED_IMAGES=1 ./cleanup.sh</code></code></pre><p>Remove local Python/ephemeral artefacts yourself when needed (<code>rm -rf .venv .pytest_cache</code>, delete <code>*.pyc</code> / <code>__pycache__</code>); see <code>.gitignore</code> for what should stay out of version control.</p><div><hr></div><h2>Success Criteria</h2><p>By end of day, your system should:</p><ul><li><p>Correctly stitch 10,000 simulated log events into sessions</p></li><li><p>Compute p95 load time within 2% of true value</p></li><li><p>Show abandonment rate broken down by funnel stage in the React dashboard</p></li><li><p>Handle new log lines with &lt; 5ms processing latency per event</p></li></ul><p><strong>Your mission</strong>: build the monitoring layer that turns a wall of log lines into the user story your product team actually needs to make decisions.</p><h2>Working Code Demo:</h2><div id="youtube2-25yrqIQfns4" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;25yrqIQfns4&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/25yrqIQfns4?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p><em>Next: Day 176 &#8212; Building executive dashboards that surface these CX metrics alongside infrastructure health for C-level views</em></p>]]></content:encoded></item><item><title><![CDATA[Day 69: GDPR Compliance — Right to Be Forgotten in Distributed Systems]]></title><description><![CDATA[What We&#8217;re Building Today]]></description><link>https://sdcourse.substack.com/p/day-69-gdpr-compliance-right-to-be</link><guid isPermaLink="false">https://sdcourse.substack.com/p/day-69-gdpr-compliance-right-to-be</guid><dc:creator><![CDATA[System Design Course]]></dc:creator><pubDate>Tue, 28 Jul 2026 08:30:16 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!I450!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3334cfca-bc6b-4c2d-ac32-7f01229b26a8_3840x2560.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>What We&#8217;re Building Today</h2><ul><li><p><strong>GDPRComplianceService</strong> &#8212; orchestrates multi-store deletion with saga pattern</p></li><li><p><strong>Kafka Tombstone Producer</strong> &#8212; crypto-shredding and null-record compaction for append-only logs</p></li><li><p><strong>Cross-store Erasure Pipeline</strong> &#8212; coordinated deletion across PostgreSQL, Redis, and Kafka</p></li><li><p><strong>Immutable Audit Trail</strong> &#8212; tamper-proof deletion records for regulatory proof</p></li></ul><div><hr></div><h2>Why This Matters</h2><blockquote><p>GDPR Article 17 gives users the legal right to have their personal data erased &#8212; within 30 days, across every system that holds it. A &#8364;20M fine (or 4% of global revenue) is the cost of getting this wrong.</p><p>The engineering problem is deeper than it sounds. Your log processing system is built on append-only principles: Kafka topics don&#8217;t support deletes, PostgreSQL rows cascade across foreign keys you may not have mapped, and Redis caches duplicate data from a dozen upstream sources. The architecture you&#8217;ve built over the past 68 days was optimized for write throughput and durability &#8212; exactly the wrong defaults for a deletion workflow.</p><p>This is where distributed systems design gets legally consequential. Netflix, Spotify, and LinkedIn all had to retrofit GDPR compliance onto event-sourced architectures that were never designed for erasure. The techniques they developed &#8212; crypto-shredding, deletion sagas, and data lineage registries &#8212; are what we implement today.</p></blockquote><div><hr></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!I450!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3334cfca-bc6b-4c2d-ac32-7f01229b26a8_3840x2560.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!I450!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3334cfca-bc6b-4c2d-ac32-7f01229b26a8_3840x2560.png 424w, /__u/substackcdn.com/image/fetch/$s_!I450!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3334cfca-bc6b-4c2d-ac32-7f01229b26a8_3840x2560.png 848w, /__u/substackcdn.com/image/fetch/$s_!I450!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3334cfca-bc6b-4c2d-ac32-7f01229b26a8_3840x2560.png 1272w, /__u/substackcdn.com/image/fetch/$s_!I450!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3334cfca-bc6b-4c2d-ac32-7f01229b26a8_3840x2560.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!I450!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3334cfca-bc6b-4c2d-ac32-7f01229b26a8_3840x2560.png" width="1456" height="971" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3334cfca-bc6b-4c2d-ac32-7f01229b26a8_3840x2560.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:971,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:3032830,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://sdcourse.substack.com/i/206261288?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3334cfca-bc6b-4c2d-ac32-7f01229b26a8_3840x2560.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!I450!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3334cfca-bc6b-4c2d-ac32-7f01229b26a8_3840x2560.png 424w, /__u/substackcdn.com/image/fetch/$s_!I450!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3334cfca-bc6b-4c2d-ac32-7f01229b26a8_3840x2560.png 848w, /__u/substackcdn.com/image/fetch/$s_!I450!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3334cfca-bc6b-4c2d-ac32-7f01229b26a8_3840x2560.png 1272w, /__u/substackcdn.com/image/fetch/$s_!I450!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3334cfca-bc6b-4c2d-ac32-7f01229b26a8_3840x2560.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div><hr></div>
      <p>
          <a href="/__u/sdcourse.substack.com/p/day-69-gdpr-compliance-right-to-be">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Week 4: Distributed Log Storage]]></title><description><![CDATA[When Log Storage Stops Being a Database Problem]]></description><link>https://sdcourse.substack.com/p/week-4-distributed-log-storage</link><guid isPermaLink="false">https://sdcourse.substack.com/p/week-4-distributed-log-storage</guid><dc:creator><![CDATA[System Design Course]]></dc:creator><pubDate>Sat, 25 Jul 2026 06:31:08 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!YGew!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14859ebc-a7fb-4697-ad3e-a9f7d994f12d_3840x2560.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1>When Log Storage Stops Being a Database Problem</h1><p>Every production observability platform eventually discovers that storing logs is not a CRUD exercise&#8212;it is a distributed systems problem. Writes must survive node failures. Partitions must exist before traffic arrives. Coordinators must know who leads. Queries must fan out without blocking. And replicas that drift must repair themselves before operators lose trust in the data.</p><h2>What We Built</h2><ul><li><p>A single Spring Boot application hosting a complete distributed log storage stack: replication gateways, partitioned ingestion, consistent-hash routing, Raft leadership, gossip membership, scatter-gather queries, tunable quorums, and anti-entropy repair</p></li><li><p>RocksDB-backed local storage with quorum replication (W=2 of 3 replicas) and Resilience4j circuit breakers on gateway paths</p></li><li><p>PostgreSQL composite partitioning (daily ranges &#215; 256 source-hash sub-partitions) with optional Kafka ingestion and batch writes</p></li><li><p>An operations dashboard at <code>/</code> aggregating Micrometer metrics, cluster membership, and a one-click demo that exercises every subsystem</p></li></ul><h2>Why This Matters</h2><blockquote><p>At Netflix or Datadog scale, log platforms ingest billions of events per day. No single node holds the data. No single query scans every shard. The engineering challenge is composing replication, partitioning, consensus, and repair into something operators can reason about under failure. This project implements that composition in one JVM&#8212;sharing H2/PostgreSQL persistence, optional Redis topology, and Prometheus export&#8212;so you can see how production patterns interact rather than studying them in isolation.</p></blockquote><h2>System Design Deep Dive</h2><p><strong>Multi-node replication with quorum writes.</strong> The storage cluster persists <code>LogEntry</code> records in RocksDB and propagates <code>ReplicationRequest</code> payloads to peers via <code>RestTemplate</code>. A write succeeds only when <code>storage.replication.write-quorum</code> acknowledgments arrive within the configured timeout. The trade-off is explicit: lower quorum improves availability during partitions; higher quorum improves durability.</p><p><strong>Consistent hashing for load balance.</strong> <code>ConsistentHashRing</code> places 150 virtual nodes per physical node using MurmurHash3, minimizing key movement when nodes join or leave&#8212;unlike naive modulo sharding that reshuffles everything. <code>DistributionMetricsService</code> exposes <code>storage.distribution.balance_score</code> so operators catch hot spots before they become incidents.</p><p><strong>Raft for ordered leadership.</strong> <code>RaftNode</code> runs election timeouts (150&#8211;300 ms) and leader heartbeats on a dedicated <code>ScheduledExecutorService</code>&#8212;sub-second timing that Spring&#8217;s <code>@Scheduled</code> pool is not tuned for. Only the leader accepts writes; followers redirect until leadership stabilizes after failover.</p><p><strong>Tunable consistency via quorums.</strong> <code>QuorumService</code> honors <code>ConsistencyLevel</code> (ONE, QUORUM, ALL) with version-vector conflict resolution. ONE minimizes latency; ALL maximizes durability at the cost of write availability during outages.</p><p><strong>Distributed Log Storage Platform Architecture</strong></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!YGew!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14859ebc-a7fb-4697-ad3e-a9f7d994f12d_3840x2560.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!YGew!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14859ebc-a7fb-4697-ad3e-a9f7d994f12d_3840x2560.png 424w, /__u/substackcdn.com/image/fetch/$s_!YGew!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14859ebc-a7fb-4697-ad3e-a9f7d994f12d_3840x2560.png 848w, /__u/substackcdn.com/image/fetch/$s_!YGew!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14859ebc-a7fb-4697-ad3e-a9f7d994f12d_3840x2560.png 1272w, /__u/substackcdn.com/image/fetch/$s_!YGew!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14859ebc-a7fb-4697-ad3e-a9f7d994f12d_3840x2560.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!YGew!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14859ebc-a7fb-4697-ad3e-a9f7d994f12d_3840x2560.png" width="1456" height="971" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/14859ebc-a7fb-4697-ad3e-a9f7d994f12d_3840x2560.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:971,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2011895,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://sdcourse.substack.com/i/206544265?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14859ebc-a7fb-4697-ad3e-a9f7d994f12d_3840x2560.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!YGew!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14859ebc-a7fb-4697-ad3e-a9f7d994f12d_3840x2560.png 424w, /__u/substackcdn.com/image/fetch/$s_!YGew!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14859ebc-a7fb-4697-ad3e-a9f7d994f12d_3840x2560.png 848w, /__u/substackcdn.com/image/fetch/$s_!YGew!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14859ebc-a7fb-4697-ad3e-a9f7d994f12d_3840x2560.png 1272w, /__u/substackcdn.com/image/fetch/$s_!YGew!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F14859ebc-a7fb-4697-ad3e-a9f7d994f12d_3840x2560.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div>
      <p>
          <a href="/__u/sdcourse.substack.com/p/week-4-distributed-log-storage">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Day 174: Create A/B Test Analysis Framework]]></title><description><![CDATA[Week 25: Business Analytics Use Cases | 254-Day Hands-On System Design Series]]></description><link>https://sdcourse.substack.com/p/day-174-create-ab-test-analysis-framework</link><guid isPermaLink="false">https://sdcourse.substack.com/p/day-174-create-ab-test-analysis-framework</guid><dc:creator><![CDATA[System Design Course]]></dc:creator><pubDate>Thu, 23 Jul 2026 08:30:27 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!G9it!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ed350fd-4bea-408b-a480-bbf1fe7484b8_3600x2320.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3>What We Build Today</h3><ul><li><p>Ingest experiment event logs from the distributed pipeline</p></li><li><p>Assign users deterministically to control/treatment groups via hash-based bucketing</p></li><li><p>Compute statistical significance (z-test, Welch&#8217;s t-test) on conversion metrics</p></li><li><p>Surface live results through a React dashboard with confidence intervals and lift %</p></li><li><p>Auto-register every experiment that appears in the log stream</p></li></ul><div><hr></div><h3>Why A/B Testing Lives in the Log Pipeline</h3><p>When Booking.com runs 1,000 simultaneous experiments, the raw signal is log data&#8212;page views, clicks, bookings&#8212;streaming through their event pipeline. Attaching the analysis framework directly to that pipeline means zero ETL delay: results update as logs arrive, not the next morning after a batch job.</p><p>Our log processor already tags every event with <code>user_id</code>, <code>session_id</code>, <code>timestamp</code>, and <code>event_type</code>. We add two fields: <code>experiment_id</code> and <code>variant</code>. The rest is statistics.</p><div><hr></div><h3>Core Concepts</h3><p><strong>Deterministic Assignment</strong> Users must land in the same variant on every visit. A simple hash solves this: <code>hash(user_id + experiment_id) % 100 &lt; split_pct &#8594; treatment</code>. No database lookup, no race condition, same result every time.</p><p><strong>Metric Aggregation</strong> Per variant we track impressions, conversions, and revenue inside a time-windowed accumulator. The window matches the experiment&#8217;s active period so stale events don&#8217;t pollute results.</p><p><strong>Statistical Testing</strong> Two-sample z-test for proportions (conversion rate) and Welch&#8217;s t-test for continuous metrics (revenue per user). p-value and 95% confidence interval update every 30 seconds. When <code>p &lt; 0.05</code> and the CI excludes zero, the result is significant.</p><p><strong>Sequential vs Fixed-Horizon</strong> Fixed-horizon tests enforce a minimum sample size before results are readable&#8212;blocking the peeking bias that inflates false-positive rates. Sequential testing (Airbnb&#8217;s method) uses alpha-spending to let teams check daily without inflating error. We support both modes via a config flag.</p><div><hr></div><h3>Architecture</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!G9it!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ed350fd-4bea-408b-a480-bbf1fe7484b8_3600x2320.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!G9it!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ed350fd-4bea-408b-a480-bbf1fe7484b8_3600x2320.png 424w, /__u/substackcdn.com/image/fetch/$s_!G9it!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ed350fd-4bea-408b-a480-bbf1fe7484b8_3600x2320.png 848w, /__u/substackcdn.com/image/fetch/$s_!G9it!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ed350fd-4bea-408b-a480-bbf1fe7484b8_3600x2320.png 1272w, /__u/substackcdn.com/image/fetch/$s_!G9it!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ed350fd-4bea-408b-a480-bbf1fe7484b8_3600x2320.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!G9it!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ed350fd-4bea-408b-a480-bbf1fe7484b8_3600x2320.png" width="1456" height="938" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0ed350fd-4bea-408b-a480-bbf1fe7484b8_3600x2320.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:938,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:536580,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://sdcourse.substack.com/i/206252737?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ed350fd-4bea-408b-a480-bbf1fe7484b8_3600x2320.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!G9it!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ed350fd-4bea-408b-a480-bbf1fe7484b8_3600x2320.png 424w, /__u/substackcdn.com/image/fetch/$s_!G9it!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ed350fd-4bea-408b-a480-bbf1fe7484b8_3600x2320.png 848w, /__u/substackcdn.com/image/fetch/$s_!G9it!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ed350fd-4bea-408b-a480-bbf1fe7484b8_3600x2320.png 1272w, /__u/substackcdn.com/image/fetch/$s_!G9it!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0ed350fd-4bea-408b-a480-bbf1fe7484b8_3600x2320.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><pre><code><code>Log Stream &#9472;&#9472;&#9658; Experiment Router &#9472;&#9472;&#9658; Metric Aggregator
                     &#9474;                      &#9474;
              Assignment Store        Stats Engine (every 30s)
                                           &#9474;
                                    Result Store (SQLite)
                                           &#9474;
                                    FastAPI REST Layer (:8174)
                                           &#9474;
                                    React Dashboard</code></code></pre>
      <p>
          <a href="/__u/sdcourse.substack.com/p/day-174-create-ab-test-analysis-framework">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Day 68: Implement Data Retention Policies]]></title><description><![CDATA[Module 2 &#183; Week 9 &#183; High Availability and Fault Tolerance]]></description><link>https://sdcourse.substack.com/p/day-68-implement-data-retention-policies</link><guid isPermaLink="false">https://sdcourse.substack.com/p/day-68-implement-data-retention-policies</guid><dc:creator><![CDATA[System Design Course]]></dc:creator><pubDate>Tue, 21 Jul 2026 07:25:48 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!-e3P!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64687e43-c80b-4544-9917-57c1c95d9dae_6600x4850.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>What We&#8217;re Building Today</h2><ul><li><p><strong>RetentionPolicyEngine</strong> &#8212; a configurable, rule-based system that maps log categories to retention windows</p></li><li><p><strong>TieredStorageManager</strong> &#8212; automatic promotion/demotion of log data across hot &#8594; warm &#8594; cold storage tiers</p></li><li><p><strong>RetentionEnforcerJob</strong> &#8212; a scheduled, idempotent enforcement job that purges expired partitions safely</p></li><li><p><strong>PolicyMetricsCollector</strong> &#8212; observability layer reporting retention lag, purge throughput, and storage reclamation rates</p></li></ul><div><hr></div><h2>Why This Matters</h2><p>Every byte you store costs money. Every byte you keep longer than legally required is a liability. Data retention is where <strong>economics, compliance, and system design collide</strong>.</p><p>At Uber&#8217;s scale, the ride-events topic alone generates terabytes per day. Without automated retention enforcement, storage costs compound weekly. Netflix&#8217;s content access logs must be purged within defined windows to satisfy GDPR and CCPA &#8212; a manual process would require a dedicated team and still be error-prone.</p><p>The naive approach &#8212; a cron job that <code>DELETE</code>s rows matching <code>WHERE created_at &lt; NOW() - INTERVAL '90 days'</code> &#8212; will destroy your database under production load. A full table scan locks rows, spikes I/O, and triggers autovacuum storms in PostgreSQL. The production pattern is fundamentally different: <strong>you don&#8217;t delete data, you expire partitions</strong>.</p><p>Tomorrow we layer GDPR &#8220;right to be forgotten&#8221; on top of today&#8217;s time-based retention. Today&#8217;s architecture is deliberately designed to make that addition surgical rather than invasive.</p><div><hr></div><h2>System Design Deep Dive</h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!-e3P!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64687e43-c80b-4544-9917-57c1c95d9dae_6600x4850.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!-e3P!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64687e43-c80b-4544-9917-57c1c95d9dae_6600x4850.png 424w, /__u/substackcdn.com/image/fetch/$s_!-e3P!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64687e43-c80b-4544-9917-57c1c95d9dae_6600x4850.png 848w, /__u/substackcdn.com/image/fetch/$s_!-e3P!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64687e43-c80b-4544-9917-57c1c95d9dae_6600x4850.png 1272w, /__u/substackcdn.com/image/fetch/$s_!-e3P!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64687e43-c80b-4544-9917-57c1c95d9dae_6600x4850.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!-e3P!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64687e43-c80b-4544-9917-57c1c95d9dae_6600x4850.png" width="498" height="365.97527472527474" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/64687e43-c80b-4544-9917-57c1c95d9dae_6600x4850.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1070,&quot;width&quot;:1456,&quot;resizeWidth&quot;:498,&quot;bytes&quot;:2898952,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://sdcourse.substack.com/i/201102447?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64687e43-c80b-4544-9917-57c1c95d9dae_6600x4850.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!-e3P!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64687e43-c80b-4544-9917-57c1c95d9dae_6600x4850.png 424w, /__u/substackcdn.com/image/fetch/$s_!-e3P!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64687e43-c80b-4544-9917-57c1c95d9dae_6600x4850.png 848w, /__u/substackcdn.com/image/fetch/$s_!-e3P!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64687e43-c80b-4544-9917-57c1c95d9dae_6600x4850.png 1272w, /__u/substackcdn.com/image/fetch/$s_!-e3P!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F64687e43-c80b-4544-9917-57c1c95d9dae_6600x4850.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="callout-block" data-callout="true"><p>Preparing for a distributed systems interview?</p><p>&#8594;<a href="https://systemdrd.com/ebooks/sdcourse-distributed-systems-interview">Download the free Interview Pack</a></p><p>&#8594; <a href="/__u/sdcourse.substack.com/subscribe">Subscribe</a> now to access source code repository - 200 + coding lessons</p></div>
      <p>
          <a href="/__u/sdcourse.substack.com/p/day-68-implement-data-retention-policies">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Week 10 : Security and Compliance — Protect logs before they become liability]]></title><description><![CDATA[What we&#8217;re building today]]></description><link>https://sdcourse.substack.com/p/week-10-security-and-compliance-protect</link><guid isPermaLink="false">https://sdcourse.substack.com/p/week-10-security-and-compliance-protect</guid><dc:creator><![CDATA[System Design Course]]></dc:creator><pubDate>Sat, 18 Jul 2026 06:30:12 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!801f!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7657f944-35e0-4173-91ea-4faab459ee73_5000x2600.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3><strong>What we&#8217;re building today</strong></h3><blockquote><p>When Datadog ingests payment logs, access is not a free-for-all&#8212;RBAC gates who sees what. Stripe encrypts PAN fields at ingest; Splunk redacts before analysts search. GDPR erasure in Shopify removes a buyer&#8217;s trail across shards. This lesson wires <strong>seven security layers</strong> into one platform.</p><p><strong>By the end you&#8217;ll have:</strong></p></blockquote><ul><li><p>JWT authentication with RBAC on log namespaces</p></li><li><p>AES-256-GCM field encryption for PII</p></li><li><p>Regex redaction before display</p></li><li><p>Hash-chained immutable audit trails</p></li><li><p>Automated retention policies</p></li><li><p>GDPR right-to-erasure workflow</p></li><li><p>SOX / HIPAA / GDPR compliance report export</p></li></ul><div><hr></div><h2><strong>Why this matters</strong></h2><ul><li><p><strong>AWS CloudTrail</strong> records who touched which resource&#8212;your audit chain mirrors that immutability model.</p></li><li><p><strong>HashiCorp Vault</strong> encrypts secrets at the field level&#8212;same idea as encrypting <code>email</code> and <code>ssn</code> in log JSON.</p></li><li><p><strong>Elastic Security</strong> redacts PAN before indexing&#8212;your redaction engine runs on the read path.</p></li><li><p><strong>SOC 2 auditors</strong> ask for retention proof&#8212;your policy engine deletes or archives on schedule.</p></li><li><p><strong>Meta GDPR tooling</strong> erases user data across stores&#8212;your erasure coordinator tracks locations and verifies completion.</p></li></ul><blockquote><p>Security is not a checkbox&#8212;it is a pipeline every log byte passes through.</p></blockquote><div><hr></div><h2><strong>Core concepts</strong></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!0WyB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d489dcc-4a7a-4577-a12e-644671a039ad_864x636.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!0WyB!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d489dcc-4a7a-4577-a12e-644671a039ad_864x636.png 424w, /__u/substackcdn.com/image/fetch/$s_!0WyB!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d489dcc-4a7a-4577-a12e-644671a039ad_864x636.png 848w, /__u/substackcdn.com/image/fetch/$s_!0WyB!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d489dcc-4a7a-4577-a12e-644671a039ad_864x636.png 1272w, /__u/substackcdn.com/image/fetch/$s_!0WyB!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d489dcc-4a7a-4577-a12e-644671a039ad_864x636.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!0WyB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d489dcc-4a7a-4577-a12e-644671a039ad_864x636.png" width="864" height="636" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3d489dcc-4a7a-4577-a12e-644671a039ad_864x636.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:636,&quot;width&quot;:864,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:90536,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://sdcourse.substack.com/i/200584156?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d489dcc-4a7a-4577-a12e-644671a039ad_864x636.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!0WyB!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d489dcc-4a7a-4577-a12e-644671a039ad_864x636.png 424w, /__u/substackcdn.com/image/fetch/$s_!0WyB!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d489dcc-4a7a-4577-a12e-644671a039ad_864x636.png 848w, /__u/substackcdn.com/image/fetch/$s_!0WyB!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d489dcc-4a7a-4577-a12e-644671a039ad_864x636.png 1272w, /__u/substackcdn.com/image/fetch/$s_!0WyB!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3d489dcc-4a7a-4577-a12e-644671a039ad_864x636.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><div><hr></div><h2><strong>Architecture</strong></h2><blockquote><p>Single <strong>FastAPI</strong> service on port <strong>8094</strong>. All security modules run in-process with SQLite persistence.</p></blockquote><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="/__u/substackcdn.com/image/fetch/$s_!z0bD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa393deb6-14cf-48f3-a938-749cd518e6ce_5900x3600.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="/__u/substackcdn.com/image/fetch/$s_!z0bD!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa393deb6-14cf-48f3-a938-749cd518e6ce_5900x3600.png 424w, /__u/substackcdn.com/image/fetch/$s_!z0bD!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa393deb6-14cf-48f3-a938-749cd518e6ce_5900x3600.png 848w, /__u/substackcdn.com/image/fetch/$s_!z0bD!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa393deb6-14cf-48f3-a938-749cd518e6ce_5900x3600.png 1272w, /__u/substackcdn.com/image/fetch/$s_!z0bD!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_webp, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa393deb6-14cf-48f3-a938-749cd518e6ce_5900x3600.png 1456w" sizes="100vw"><img src="/__u/substackcdn.com/image/fetch/$s_!z0bD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa393deb6-14cf-48f3-a938-749cd518e6ce_5900x3600.png" width="590" height="359.83516483516485" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a393deb6-14cf-48f3-a938-749cd518e6ce_5900x3600.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:888,&quot;width&quot;:1456,&quot;resizeWidth&quot;:590,&quot;bytes&quot;:891886,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://sdcourse.substack.com/i/200584156?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa393deb6-14cf-48f3-a938-749cd518e6ce_5900x3600.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="/__u/substackcdn.com/image/fetch/$s_!z0bD!, /__u/sdcourse.substack.com/w_424, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa393deb6-14cf-48f3-a938-749cd518e6ce_5900x3600.png 424w, /__u/substackcdn.com/image/fetch/$s_!z0bD!, /__u/sdcourse.substack.com/w_848, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa393deb6-14cf-48f3-a938-749cd518e6ce_5900x3600.png 848w, /__u/substackcdn.com/image/fetch/$s_!z0bD!, /__u/sdcourse.substack.com/w_1272, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa393deb6-14cf-48f3-a938-749cd518e6ce_5900x3600.png 1272w, /__u/substackcdn.com/image/fetch/$s_!z0bD!, /__u/sdcourse.substack.com/w_1456, /__u/sdcourse.substack.com/c_limit, /__u/sdcourse.substack.com/f_auto, /__u/sdcourse.substack.com/q_auto:good, /__u/sdcourse.substack.com/fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa393deb6-14cf-48f3-a938-749cd518e6ce_5900x3600.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="callout-block" data-callout="true"><p>Preparing for a distributed systems interview?</p><p>&#8594;<a href="https://systemdrd.com/ebooks/sdcourse-distributed-systems-interview">Download the free Interview Pack</a></p><p>&#8594; <a href="/__u/sdcourse.substack.com/subscribe">Subscribe</a> now to access source code repository - 200 + coding lessons</p></div>
      <p>
          <a href="/__u/sdcourse.substack.com/p/week-10-security-and-compliance-protect">
              Read more
          </a>
      </p>
   ]]></content:encoded></item></channel></rss>