<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Hurdad Ashouri</title>
    <description>The latest articles on DEV Community by Hurdad Ashouri (@hurdadd).</description>
    <link>https://dev.to/hurdadd</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3981736%2F1643f855-273f-44d5-88a0-39d5f487ced4.png</url>
      <title>DEV Community: Hurdad Ashouri</title>
      <link>https://dev.to/hurdadd</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hurdadd"/>
    <language>en</language>
    <item>
      <title>A 100K-token prompt is a denial-of-service attack you paid for</title>
      <dc:creator>Hurdad Ashouri</dc:creator>
      <pubDate>Tue, 20 Oct 2026 17:43:51 +0000</pubDate>
      <link>https://dev.to/hurdadd/a-100k-token-prompt-is-a-denial-of-service-attack-you-paid-for-9j5</link>
      <guid>https://dev.to/hurdadd/a-100k-token-prompt-is-a-denial-of-service-attack-you-paid-for-9j5</guid>
      <description>&lt;p&gt;A coding agent takes 11 seconds to rename a variable.&lt;/p&gt;

&lt;p&gt;The model is fine. The GPU is fine. The p99 is 11 seconds because one user pasted a 100K-token repository into the same pool, and everyone else's decode step is stuck behind its prefill.&lt;/p&gt;

&lt;p&gt;That's the part of LLM serving that never shows up in the demo. Your p99 is not a property of your model. It's a property of your batch.&lt;/p&gt;

&lt;p&gt;Nobody is attacking you. The longest prompt in the batch is.&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;

&lt;p&gt;You own LLM serving. The traffic is not toy chat. It's coding agents, RAG, long system prompts, and tool outputs. The 100K-token class is routine, and prefixes repeat. Prefill and decode share the same GPU pool. Tail latency blows up under load. GPUs are expensive.&lt;/p&gt;

&lt;p&gt;Design the serving path:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Where does the router live, and how does it guess cold versus warm?&lt;/li&gt;
&lt;li&gt;Which tier pays full compute, and which tier only reads cache?&lt;/li&gt;
&lt;li&gt;How many KV levels do you keep?&lt;/li&gt;
&lt;li&gt;How do you isolate decode from prefill noise?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Take a minute before you read the solution. The interesting part is not the boxes. It's what happens when traffic turns cold.&lt;/p&gt;

&lt;h2&gt;
  
  
  An LLM request is two workloads
&lt;/h2&gt;

&lt;p&gt;A normal web request is one unit of work. An LLM request is two, and they scale in opposite directions.&lt;/p&gt;

&lt;p&gt;Prefill runs the prompt through the model. It is a big matrix multiply, compute-bound, and bursty. A 100K-token prompt can occupy a batch slot for seconds.&lt;/p&gt;

&lt;p&gt;Decode generates one token at a time. It is memory-bandwidth-bound, and every token has a deadline. The user feels the gap between tokens.&lt;/p&gt;

&lt;p&gt;Put them in one continuous batch and the long prefill becomes a head-of-line block. Decode tokens for other requests sit in the queue. The average looks fine because most requests are short. The p99 belongs to the requests that arrived behind the 100K-token prompt.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3e9ii10keexg9kac2zdp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3e9ii10keexg9kac2zdp.png" alt="Timeline comparing a mixed GPU pool with split prefill and decode pools" width="799" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is why "add more GPUs" can make the p99 worse. You added capacity to a queue that is still mixing two workloads. The long prefill is still in front of the short decode.&lt;/p&gt;

&lt;p&gt;And the context window makes this structural, not a bug. Coding agents, RAG, and long system prompts push more traffic into the 100K class every quarter. The tail is not an edge case anymore. It's the product.&lt;/p&gt;

&lt;p&gt;Streaming makes it worse. Your SLO is not one latency number. It is TTFT, time to first token, and TPOT, time per output token. A user will forgive a two-second TTFT. They will not forgive a decode that stutters every 200 ms.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix, in four moves
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Split the pools
&lt;/h3&gt;

&lt;p&gt;Prefill and decode get separate GPU pools. Prefill can use tensor parallelism and large batches. Decode gets a smaller, latency-optimized pool. The KV cache is the handoff between them. No shared batch, no shared queue.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Route on cache warmth
&lt;/h3&gt;

&lt;p&gt;The router hashes the prompt prefix, or walks a radix tree, and asks which replica already holds the KV blocks for that prefix. A warm prompt goes to the replica that can read the cache. A cold prompt goes to the prefill tier, which pays full compute and writes new blocks.&lt;/p&gt;

&lt;p&gt;The router's job is cache locality plus load, not round-robin. In an agent product this matters more than in chat: the system prompt, tool definitions, and earlier turns are a prefix you can reuse. Miss that, and you pay prefill again on every turn.&lt;/p&gt;

&lt;p&gt;Agents make it harder. Every tool call appends output, so the prefix grows and the cache key changes. A cache that ignores the tool transcript will miss. Track prefix hit rate per route, not a single global hit rate. A healthy global number can hide a coding-agent route that is always cold.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Tier the KV cache
&lt;/h3&gt;

&lt;p&gt;L1 in GPU HBM, L2 in host DRAM, L3 in a distributed cache over RDMA. Hot blocks migrate up. Cold blocks go down. In the reported setup, a warm 100K-token context goes from seconds of compute to a few hundred milliseconds of TTFT, because the expensive part already happened.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Isolate decode and control admission
&lt;/h3&gt;

&lt;p&gt;Continuous batching for decode. Chunked prefill or a separate prefill queue. Separate SLOs: TTFT for prefill, TPOT or ITL for decode. Admission control so a cold burst cannot borrow decode capacity. The reported setup caps max in-flight requests at 24.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F44nb5ong3qyvpc7hx357.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F44nb5ong3qyvpc7hx357.png" alt="Cache-aware disaggregated serving: cold and warm paths, three KV tiers, isolated decode" width="800" height="467"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Read the diagram from left to right. Start at the client. The prompt goes to the cache-aware router. The router forks: cold goes up to the pre-prefill tier, warm goes down to the prefill pool. The cold path writes new KV blocks into the cache. The warm path reads them back. The cache has three tiers: L1 GPU HBM, L2 host DRAM, L3 RDMA cluster. The prefill pool hands the KV state to the decode pool, and decode streams tokens to the client.&lt;/p&gt;

&lt;p&gt;Notice what is missing. There is no arrow from the cold path into decode. The long compute cannot sit in the decode queue.&lt;/p&gt;

&lt;h2&gt;
  
  
  The numbers, with the caveat
&lt;/h2&gt;

&lt;p&gt;Together AI reports 1.1 to 1.15 QPS per GPU for CPD, against a 0.75 to 0.8 baseline for the 2P1D setup. That is roughly 35 to 40 percent more sustained throughput under a tail-sensitive SLO, on B200 GPUs. Treat those as that source's reported numbers, not universal law. The useful part is the shape, not the decimal.&lt;/p&gt;

&lt;h2&gt;
  
  
  The math that makes it real
&lt;/h2&gt;

&lt;p&gt;Take a 70B model with grouped-query attention: 80 layers, 8 KV heads, head_dim 128, fp16.&lt;/p&gt;

&lt;p&gt;KV per token = 2 × 80 × 8 × 128 × 2 bytes = 320 KiB.&lt;/p&gt;

&lt;p&gt;A 100K-token request = 320 KiB × 100,000 ≈ 32 GB.&lt;/p&gt;

&lt;p&gt;The weights are about 140 GB in fp16. On a node with 4 × 192 GB, the weights take 140 GB and leave about 628 GB for KV. That is roughly 19 concurrent 100K-token requests before fragmentation, activations and cache tiers.&lt;/p&gt;

&lt;p&gt;That is the real scheduling budget. Not "how many requests per second." How many long contexts can sit in memory at once. The KV cache is not an optimization. It is the thing you are actually selling.&lt;/p&gt;

&lt;h2&gt;
  
  
  The follow-up that separates answers
&lt;/h2&gt;

&lt;p&gt;Almost every prompt is cold. Which piece breaks first?&lt;/p&gt;

&lt;p&gt;The prefill pool saturates first. Then the cache-fill path: every cold request writes new KV blocks, and the distributed cache's write bandwidth becomes the bottleneck. The router's hit-rate assumption collapses. TTFT dies. Decode isolation still protects TPOT, which is the point: a good design fails in one lane, not both.&lt;/p&gt;

&lt;p&gt;The fix is not more GPUs. It is admission control with deadlines, a reserved prefill lane for cold bursts, and a degrade path: shorter context, smaller model, or reject with a retry hint. If cold prefill borrows decode capacity, you lose both SLOs.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a good answer demonstrates
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Why mixing prefill and decode hurts the tail.&lt;/li&gt;
&lt;li&gt;Cold versus warm routing and the cost of each branch.&lt;/li&gt;
&lt;li&gt;The KV hierarchy and how decode stays isolated.&lt;/li&gt;
&lt;li&gt;The failure mode when the cache misses.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;This exercise is one of 47 in the DDIA Study Lab. The case diagram and the rest of the lab are in the repo. The exercise is deliberately unsolved, so bring your own design.&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/Hurdadd/ddia-study-lab" rel="noopener noreferrer"&gt;https://github.com/Hurdadd/ddia-study-lab&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Site: &lt;a href="https://ddia-study-lab.vercel.app/" rel="noopener noreferrer"&gt;https://ddia-study-lab.vercel.app/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you try it, tell me where your design breaks. The cold-traffic answer is the one I care about.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>systemdesign</category>
      <category>performance</category>
    </item>
    <item>
      <title>DDIA is a great book. It is a bad lab.</title>
      <dc:creator>Hurdad Ashouri</dc:creator>
      <pubDate>Mon, 19 Oct 2026 17:44:22 +0000</pubDate>
      <link>https://dev.to/hurdadd/ddia-is-a-great-book-it-is-a-bad-lab-bm2</link>
      <guid>https://dev.to/hurdadd/ddia-is-a-great-book-it-is-a-bad-lab-bm2</guid>
      <description>&lt;p&gt;DDIA is the best distributed systems book I have read. I finished the second edition with a head full of vocabulary and no way to check whether I actually understood any of it.&lt;/p&gt;

&lt;p&gt;That is not a knock on the book. It has dozens of figures, real company stories, and code for a small key-value store. What it does not have is a feedback loop. No exercises. No answer key. No failure timeline you can step through. No numbers you can change and watch break.&lt;/p&gt;

&lt;p&gt;The gap shows up in interviews. You can explain linearizability and still freeze when the old leader comes back with a stale lease. You can describe two-phase commit, or 2PC, and still not know what a participant does after it votes yes and the coordinator disappears.&lt;/p&gt;

&lt;p&gt;So I built the lab I wanted while reading it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The uncomfortable part
&lt;/h2&gt;

&lt;p&gt;Reading DDIA cover to cover is not a flex. Most people finish it with a warm feeling and no changed decisions. I did that the first time.&lt;/p&gt;

&lt;p&gt;The book is a map. Maps are useful. But you do not learn a city by reading a map, and you do not learn a distributed system by nodding at a diagram. You learn it when you have to choose between two designs, defend the choice, and find the failure that kills it.&lt;/p&gt;

&lt;p&gt;Most study notes make this worse. They compress a chapter into definitions and arrows, then delete the part where you have to think. You finish the summary and feel productive. You still cannot answer the interview question.&lt;/p&gt;

&lt;p&gt;The exercises here are not "summarize chapter 6." They are "the leader dies here, the client retries there, show me the duplicate and the fix."&lt;/p&gt;

&lt;p&gt;If you have read DDIA and never changed a design because of it, you read a good book. You did not practice.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is in it
&lt;/h2&gt;

&lt;p&gt;14 chapters, in English and Persian, with the full content in both.&lt;/p&gt;

&lt;p&gt;155 chapter diagrams. 89 are interactive step-throughs: write paths, failovers, quorums, 2PC, compaction. 66 are hand-built concept diagrams: UML, timelines, decision trees. Every diagram is followed by a teacher-style walkthrough that reads the figure step by step. Not a caption.&lt;/p&gt;

&lt;p&gt;302 worked examples. Each one has the same shape: initial state, operation, result and reason. The numbers are teaching numbers, labeled as such.&lt;/p&gt;

&lt;p&gt;47 design exercises with full worked solutions, plus 118 diagrams inside those solutions. The exercises are leveled for foundation, senior and staff roles, and tagged by priority.&lt;/p&gt;

&lt;p&gt;A capstone that turns the chapters into one AI platform design: requirements, estimates, 15 ADRs, recovery targets and a developer journey.&lt;/p&gt;

&lt;h2&gt;
  
  
  The state that made 2PC click
&lt;/h2&gt;

&lt;p&gt;Two-phase commit looks simple on a whiteboard. Prepare, commit, done. The hard part is the gap between a yes vote and a decision.&lt;/p&gt;

&lt;p&gt;Initial state: participant A voted yes. It is prepared, holds locks, and waits for the coordinator.&lt;/p&gt;

&lt;p&gt;Operation: the coordinator disappears. A times out.&lt;/p&gt;

&lt;p&gt;Result: A cannot commit or abort on its own. It keeps the locks until it learns the decision, or until an operator decides by hand. A timeout is not information. Releasing the locks early breaks atomicity.&lt;/p&gt;

&lt;p&gt;That is the kind of state I could not practice by reading. I could nod at it. I could not reason through it under pressure.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhbvlvt36m7ddw05cdv26.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhbvlvt36m7ddw05cdv26.png" alt="Two-phase commit stuck in doubt: vote yes, coordinator disappears, locks stay" width="799" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The diagram is not decoration. It is the exercise. You trace the arrows, find the two points of no return, and see why "just retry" is not a recovery strategy.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it is not
&lt;/h2&gt;

&lt;p&gt;It is not a replacement for DDIA. The book is the source. The lab is the practice.&lt;/p&gt;

&lt;p&gt;It is not a production system. The capstone is a design workshop. Everything proposed is labeled proposed. There are no fake benchmarks or invented SLOs.&lt;/p&gt;

&lt;p&gt;It is not a coding course. The 47 exercises are design and interview drills, not "implement a B-tree in 300 lines." Some have optional lab extensions, but the main path is reasoning on a whiteboard.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why two languages
&lt;/h2&gt;

&lt;p&gt;I read in English and explain in Persian. Most DDIA notes pick one language. Every chapter page has a toggle, and the same content exists in both. If you are learning technical English, the side-by-side version is useful on its own.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;Start with chapter 4 if you want storage engines, or chapter 10 if you want consensus. The exercise audit page lists the level and evidence for every drill.&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://github.com/Hurdadd/ddia-study-lab" rel="noopener noreferrer"&gt;https://github.com/Hurdadd/ddia-study-lab&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Site: &lt;a href="https://ddia-study-lab.vercel.app/" rel="noopener noreferrer"&gt;https://ddia-study-lab.vercel.app/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If this helps you prepare, star the repo. If a diagram is wrong, open an issue. I would rather fix the diagram than keep the star.&lt;/p&gt;

</description>
      <category>ddia</category>
      <category>distributedsystems</category>
      <category>systemdesign</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
