<?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[Ammar Husain]]></title><description><![CDATA[Ammar Husain]]></description><link>https://ammarhusain.substack.com</link><image><url>https://substackcdn.com/image/fetch/$s_!bYzc!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4a7d1f59-df58-40cd-8f9a-5c9ae88ce181_231x207.jpeg</url><title>Ammar Husain</title><link>https://ammarhusain.substack.com</link></image><generator>Substack</generator><lastBuildDate>Wed, 08 Apr 2026 22:22:18 GMT</lastBuildDate><atom:link href="https://ammarhusain.substack.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Ammar Husain]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[ammarhusain@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[ammarhusain@substack.com]]></itunes:email><itunes:name><![CDATA[Ammar Husain]]></itunes:name></itunes:owner><itunes:author><![CDATA[Ammar Husain]]></itunes:author><googleplay:owner><![CDATA[ammarhusain@substack.com]]></googleplay:owner><googleplay:email><![CDATA[ammarhusain@substack.com]]></googleplay:email><googleplay:author><![CDATA[Ammar Husain]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[Beyond Caching: Content Delivery Networks]]></title><description><![CDATA[The CDN Puzzle: Solving Latency, Load, and Global Delivery]]></description><link>https://ammarhusain.substack.com/p/beyond-caching-content-delivery-networks</link><guid isPermaLink="false">https://ammarhusain.substack.com/p/beyond-caching-content-delivery-networks</guid><dc:creator><![CDATA[Ammar Husain]]></dc:creator><pubDate>Fri, 20 Mar 2026 12:41:17 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!V_-6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bd3c14f-d90b-426f-8b33-801a283ac3f4_1536x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3>Introduction</h3><p>Consider a user in Australia browsing their social media feed to catch up with friends in <em>Europe </em>and <em>America</em>. The media shared by friends takes a considerable time to load despite the user having a reasonably fast internet connection&#8202;&#8212;&#8202;while the same content loads instantly for those browsing from within <em>Europe</em>.</p><p>Consider another user in <em>America </em>trying to watch a live concert in <em>Europe </em>on their device. The broadcast is interrupted briefly but frequently. However, for the <em>European </em>audience, the broadcast is seamless.</p><p>In both cases, users outside the geography faced delays in accessing content over the internet due to <em>increased round-trip time and additional network hops</em>. This happens despite users having reasonably fast internet connections and providers having servers with enough capability to serve traffic and withstand spikes.</p><p>To provide a fair user experience, content providers need to ensure the geographical disadvantage is blunted by serving content locally. This is the core problem that <strong>Content Delivery Networks (CDNs)</strong><em><strong> </strong></em>solve&#8202;&#8212;&#8202;<em>bringing content closer to the user by caching and serving it from geographically distributed edge servers</em>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!V_-6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bd3c14f-d90b-426f-8b33-801a283ac3f4_1536x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!V_-6!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bd3c14f-d90b-426f-8b33-801a283ac3f4_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!V_-6!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bd3c14f-d90b-426f-8b33-801a283ac3f4_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!V_-6!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bd3c14f-d90b-426f-8b33-801a283ac3f4_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!V_-6!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bd3c14f-d90b-426f-8b33-801a283ac3f4_1536x1024.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!V_-6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bd3c14f-d90b-426f-8b33-801a283ac3f4_1536x1024.png" width="1456" height="971" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3bd3c14f-d90b-426f-8b33-801a283ac3f4_1536x1024.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;:2923717,&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://ammarhusain.substack.com/i/191576421?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bd3c14f-d90b-426f-8b33-801a283ac3f4_1536x1024.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!V_-6!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bd3c14f-d90b-426f-8b33-801a283ac3f4_1536x1024.png 424w, https://substackcdn.com/image/fetch/$s_!V_-6!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bd3c14f-d90b-426f-8b33-801a283ac3f4_1536x1024.png 848w, https://substackcdn.com/image/fetch/$s_!V_-6!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bd3c14f-d90b-426f-8b33-801a283ac3f4_1536x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!V_-6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3bd3c14f-d90b-426f-8b33-801a283ac3f4_1536x1024.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 role="img" 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><title></title><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>Content Delivery Network (CDN)</h3><h4>Definition</h4><p>Formally, a <strong><a href="https://en.wikipedia.org/wiki/Content_delivery_network">Content Delivery Network (CDN)</a></strong> is a geographically distributed network of <a href="https://en.wikipedia.org/wiki/Proxy_server">proxy servers</a> and corresponding <a href="https://en.wikipedia.org/wiki/Data_center">data centers</a>.</p><p>The primary purpose of a <strong>CDN </strong>is to <em>provide content at high speed</em>. Thus, it shouldn&#8217;t be considered as a replacement of a <em>web host</em> but a service to help traditional web host overcome various limitations.</p><h4>Core Components</h4><p>A typical CDN consists of below core components &#8594;</p><ul><li><p><em>Origin Server</em> &#8594; The primary server where the original, authoritative version of content resides. This server is the <em>web host</em>. Without a CDN, every user request would hit this server directly, regardless of their location.</p></li><li><p><em>Edge Servers</em> &#8594; CDN <em>cache </em>servers deployed at the &#8220;<em>edge</em>&#8221; of the network, physically closer to end users. They store <em>cached copies</em> of content. When a user requests a resource, the nearest edge server serves it, drastically reducing <em>round-trip time (RTT)</em>.</p></li><li><p><em>Point of Presence (PoP)</em> &#8594; A <em>PoP </em>is a physical data center location housing a <em>cluster of edge servers</em>. Major <em>CDN </em>providers operate hundreds of <em>PoPs </em>worldwide. Each <em>PoP </em>serves users in its geographic vicinity&#8202;&#8212;&#8202;think of them as <em>regional cache</em> of content.</p></li><li><p><em>Internet Exchange Points (IXPs) &#8594;</em> These are physical locations where different networks (<em>ISPs, CDNs, cloud providers</em>) interconnect and exchange traffic. CDNs strategically collocate at IXPs to peer directly with ISPs, minimizing network hops and improving delivery speed.</p></li></ul><h4>Traffic Management</h4><p>A typical CDN utilizes below to manage traffic &#8594;</p><ul><li><p><em>Global Server Load Balancing (GSLB) </em>&#8594; A DNS-based mechanism that intelligently routes user requests to the optimal PoP based on factors like geographic proximity, server health, network congestion, and current load.</p></li><li><p><em>Selector (Request Routing)</em> &#8594; The decision logic&#8202;&#8212;&#8202;often working alongside GSLB&#8202;&#8212;&#8202;that determines which edge server within a PoP handles a specific request, factoring in content availability, server capacity, and session affinity.</p></li></ul><h4>Key Concepts</h4><ul><li><p><em>Offloading &#8594; </em>The percentage of requests served directly by edge servers without going back to the origin. A high cache-hit ratio (<em>e.g., 95%</em>) means significant offloading&#8202;&#8212;&#8202;<em>reducing origin bandwidth, compute costs, and the risk of origin overload</em>.</p></li><li><p><em>Footprint </em>&#8594; Refers to the CDN&#8217;s global reach&#8202;&#8212;&#8202;<em>the total number and distribution of PoPs, edge servers, and network capacity</em>. A larger footprint means better coverage, lower latency for diverse user bases, and greater resilience against regional failures.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!2iU1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43a4d549-019b-4d00-9180-39f29b88efcb_1024x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!2iU1!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43a4d549-019b-4d00-9180-39f29b88efcb_1024x1536.png 424w, https://substackcdn.com/image/fetch/$s_!2iU1!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43a4d549-019b-4d00-9180-39f29b88efcb_1024x1536.png 848w, https://substackcdn.com/image/fetch/$s_!2iU1!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43a4d549-019b-4d00-9180-39f29b88efcb_1024x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!2iU1!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43a4d549-019b-4d00-9180-39f29b88efcb_1024x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!2iU1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43a4d549-019b-4d00-9180-39f29b88efcb_1024x1536.png" width="1024" height="1536" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/43a4d549-019b-4d00-9180-39f29b88efcb_1024x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1536,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1225667,&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://ammarhusain.substack.com/i/191576421?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43a4d549-019b-4d00-9180-39f29b88efcb_1024x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!2iU1!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43a4d549-019b-4d00-9180-39f29b88efcb_1024x1536.png 424w, https://substackcdn.com/image/fetch/$s_!2iU1!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43a4d549-019b-4d00-9180-39f29b88efcb_1024x1536.png 848w, https://substackcdn.com/image/fetch/$s_!2iU1!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43a4d549-019b-4d00-9180-39f29b88efcb_1024x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!2iU1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F43a4d549-019b-4d00-9180-39f29b88efcb_1024x1536.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 role="img" 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><title></title><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>In Summary &#8594; Users hit a nearby edge server at a PoP (<em>often at an IXP</em>), routed there by GSLB/selectors, offloading traffic from your origin&#8202;&#8212;&#8202;all enabled by the CDN&#8217;s global footprint.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://ammarhusain.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">Thanks for reading! Subscribe for free to receive new posts and support my work.</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><h4>Type of CDNs</h4><p>CDNs can be classified based on the networking techniques they use to route and deliver content &#8594;</p><ol><li><p><em>Anycast-Based CDN</em> &#8594; Uses <a href="https://en.wikipedia.org/wiki/Border_Gateway_Protocol">Border Gateway Protocol</a> <em>Anycast </em>routing, where the same IP address is announced from multiple geographically distributed PoPs. Thus, when a user sends a request, the network&#8217;s BGP routing directs the packet to the nearest (<em>in terms of network hops/latency</em>) server advertising that IP. This is simple, fast failover, resilient to DDoS attacks (<em>traffic is naturally distributed</em>) and utilized by <em>Cloudflare, Google Cloud CDN</em>.</p></li><li><p><em>DNS-Based CDN</em> &#8594; Uses DNS resolution to direct users to the optimal edge server. Thus, a user request is resolved to a domain, the CDN&#8217;s authoritative DNS server returns the IP of the closest or least-loaded edge server based on the user&#8217;s location (<em>via the resolver&#8217;s IP or EDNS Client Subnet</em>). This provides fine-grained control over routing decisions (<em>can factor in server load, geography, health</em>). However, it suffers from DNS caching/TTL delays; routing is based on the DNS resolver&#8217;s location, not always the end user&#8217;s. Utilized by Akamai and Amazon CloudFront.</p></li><li><p><em>Unicast-Based CDN</em> &#8594; Uses a unique IP address for each edge server, and traffic is directed via <em>DNS or application-layer logic</em>. The CDN&#8217;s control plane decides which specific server IP to hand back for a given request. Although this provides full control over which server handles which request, it requires more complex routing logic at the application/DNS layer.</p></li><li><p><em>Multicast-Based CDN</em> &#8594; Uses IP Multicast to deliver the same content to multiple recipients simultaneously. A single stream is sent and replicated at network routers to reach all subscribers&#8202;&#8212;&#8202;avoids sending duplicate copies. This is extremely efficient for live streaming/broadcast scenarios. However, due to limited multicast support across the public internet it is mostly used within managed/private networks (<em>IPTV, enterprise</em>).</p></li><li><p><em>Peer-to-Peer (P2P) Hybrid CDN</em> &#8594; Combines traditional CDN edge servers with P2P networking among end users. Users who have already downloaded content share chunks with nearby peers, <em>reducing load on origin/edge servers</em>. This scales massively for popular content; reduces bandwidth costs. However, it heavily depends on peer availability this latency can vary. Moreover, it has potential security/privacy concerns.</p></li><li><p><em>Application-Layer (Overlay) CDN</em> &#8594; Builds a logical overlay network on top of the existing internet infrastructure, using application-layer routing. Edge servers communicate with each other through an optimized overlay topology (<em>not relying on default BGP paths</em>). Requests are routed through intermediate CDN nodes for optimal performance. It can optimize around congestion, packet loss, and suboptimal BGP routes with added complexity though. This also requires a sophisticated control plane.</p></li></ol><h4>Benefits &amp; Use cases</h4><p>CDN provides below benefits &#8594;</p><ul><li><p><em>Reduced Latency</em> &#8594; CDNs cache content on edge servers geographically closer to users, drastically reducing round-trip time.</p></li><li><p><em>High Availability &amp; Redundancy &#8594; </em>Traffic is distributed across multiple servers, so if one node fails, others handle requests seamlessly.</p></li><li><p><em>Scalability </em>&#8594; CDNs absorb traffic spikes (<em>e.g., flash sales, viral content</em>) without overloading the origin server.</p></li><li><p><em>Bandwidth Cost Savings</em> &#8594; Caching reduces the number of requests hitting the origin, lowering bandwidth and infrastructure costs.</p></li><li><p><em>Security </em>&#8594; Many CDNs offer DDoS mitigation, WAF (<em>Web Application Firewall</em>), and TLS termination at the edge.</p></li><li><p><em>Improved SEO &#8594; </em>Faster page loads positively impact search engine rankings.</p></li></ul><p>Common Use Cases for CDN are &#8594;</p><ul><li><p><em>Static asset delivery</em> &#8594; Images, CSS, JavaScript, fonts, and videos (<em>e.g. social media sites</em>).</p></li><li><p><em>Video/audio streaming &#8594; </em>Low-latency media delivery at scale (<em>e.g., Netflix, YouTube</em>).</p></li><li><p><em>Software distribution</em> &#8594; Serving binaries, patches, and updates (e.g., OS updates, game downloads).</p></li><li><p><em>API acceleration</em> &#8594; Caching API responses for read-heavy workloads.</p></li><li><p><em>E-commerce </em>&#8594; Handling global traffic with consistent performance during peak events.</p></li></ul><p>In short, CDNs are essential for any application that serves content to a geographically distributed audience and needs fast, reliable delivery.</p><p>However, its imperative to know <em>When Not to Use a CDN &#8594;</em></p><ul><li><p><em>Highly dynamic/personalized content &#8594; </em>User-specific dashboards, real-time data feeds, or authenticated API responses gain minimal caching benefit.</p></li><li><p><em>Real-time applications &#8594;</em> WebSocket connections, live gaming, or chat systems require persistent connections poorly suited to CDN architecture.</p></li><li><p><em>Geographically concentrated users</em> &#8594; If your audience is near the origin server, a CDN adds unnecessary intermediary hops.</p></li><li><p><em>Sensitive/regulated data &#8594; </em>Distributing confidential or compliance-bound content (e.g., healthcare, financial) across third-party edge servers raises security and legal concerns.</p></li><li><p><em>Small-scale projects &#8594; </em>The operational complexity and cost outweigh performance gains for low-traffic applications.</p></li></ul><h4>Limitations</h4><p>CDNs cache content at edge servers, but cache invalidation is complex&#8202;&#8212;&#8202;stale content can persist after updates. They add cost overhead (<em>bandwidth fees, per-request charges</em>) that may not justify the benefit for low-traffic sites.</p><p>CDNs offer limited control over edge server behavior and can introduce debugging complexity when issues arise across distributed nodes. They also have origin dependency&#8202;&#8212;&#8202;if your origin server fails, the CDN can only serve cached content until it expires.</p><p>Additionally, latency for cache misses can actually be higher than direct origin requests due to extra routing hops.</p><div><hr></div><h4>Conclusion</h4><p><em>Content Delivery Networks</em> have become a cornerstone of modern web architecture, ensuring that applications deliver <em>fast, reliable, and secure experiences to users regardless of geography</em>.</p><p>By caching content closer to end users, intelligently routing traffic, and providing resilience against spikes and failures, CDNs address the fundamental challenges of latency and scalability on the internet.</p><p>While they offer significant benefits&#8202;&#8212;&#8202;<em>ranging from performance gains to cost savings and security enhancements&#8202;</em>&#8212;&#8202;CDNs are not a one-size-fits-all solution. Their limitations, such as <em>cache invalidation complexity and added operational overhead</em>, must be carefully weighed against project needs.</p><p>For software engineers and architects, understanding when and how to leverage CDNs is critical to building systems that <em>balance efficiency, reliability, and cost-effectiveness in a globally connected world</em>.</p><div><hr></div><h4>References and Further Reads</h4><ul><li><p><a href="https://en.wikipedia.org/wiki/Content_delivery_network">CDN (Wikipedia)</a></p></li><li><p><a href="https://www.cloudflare.com/learning/cdn/what-is-a-cdn/">Couldfare&#8202;&#8212;&#8202;What is CDN</a></p></li><li><p><a href="https://www.akamai.com/glossary/what-is-a-cdn">Akamai&#8202;&#8212;&#8202;What is CDN</a></p></li><li><p><a href="https://blog.leaseweb.com/2025/01/15/cdns-in-action-real-world-success-stories/">CDN Success Stories</a></p></li><li><p><a href="https://blog.blazingcdn.com/en-us/how-a-multi-cdn-strategy-saved-us-from-costly-downtime-1">Case Study&#8202;&#8212;&#8202;Multi CDN</a></p></li></ul>]]></content:encoded></item><item><title><![CDATA[Consensus in Distributed Systems: Understanding the Raft Algorithm]]></title><description><![CDATA[Raft: Why Simplicity Wins in Distributed Consensus]]></description><link>https://ammarhusain.substack.com/p/consensus-in-distributed-systems</link><guid isPermaLink="false">https://ammarhusain.substack.com/p/consensus-in-distributed-systems</guid><dc:creator><![CDATA[Ammar Husain]]></dc:creator><pubDate>Sat, 14 Feb 2026 10:46:19 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!VG2v!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F798ce270-1cea-434f-b566-3d252328a144_1024x1536.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3>Introduction</h3><p>Consider a group of friends planning a weekend outing. To make the trip successful, they need consensus on the <em>location, schedule, and budget</em>. Typically, one person is chosen as the leader&#8202;&#8212;&#8202;responsible for decisions, tracking expenses, and keeping everyone informed, including any new members who join later. If the leader steps down, the group elects another to maintain <em>continuity</em>.</p><p>In distributed computing, clusters of servers face a similar challenge&#8202;&#8212;&#8202; they must agree on shared state and decisions. This is achieved through <a href="https://en.wikipedia.org/wiki/Consensus_%28computer_science%29">C</a><strong><a href="https://en.wikipedia.org/wiki/Consensus_%28computer_science%29">onsensus Protocols</a></strong>. Among the most well-known are <strong>Viewstamped Replication (VSR), Zookeeper Atomic Broadcast (<a href="https://en.wikipedia.org/wiki/Apache_ZooKeeper">ZAB</a>)</strong>, <strong><a href="https://en.wikipedia.org/wiki/Paxos_%28computer_science%29">Paxos</a></strong> and <strong><a href="https://en.wikipedia.org/wiki/Raft_%28algorithm%29">Raft</a></strong>. In this article, we will explore Raft&#8202;&#8212;&#8202;<em>designed to be more understandable while ensuring reliability in distributed systems</em>.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://ammarhusain.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">Thanks for reading! Subscribe for free to receive new posts and support my work.</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><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!VG2v!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F798ce270-1cea-434f-b566-3d252328a144_1024x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!VG2v!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F798ce270-1cea-434f-b566-3d252328a144_1024x1536.png 424w, https://substackcdn.com/image/fetch/$s_!VG2v!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F798ce270-1cea-434f-b566-3d252328a144_1024x1536.png 848w, https://substackcdn.com/image/fetch/$s_!VG2v!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F798ce270-1cea-434f-b566-3d252328a144_1024x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!VG2v!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F798ce270-1cea-434f-b566-3d252328a144_1024x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!VG2v!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F798ce270-1cea-434f-b566-3d252328a144_1024x1536.png" width="1024" height="1536" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/798ce270-1cea-434f-b566-3d252328a144_1024x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1536,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2057383,&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://ammarhusain.substack.com/i/187940075?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F798ce270-1cea-434f-b566-3d252328a144_1024x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!VG2v!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F798ce270-1cea-434f-b566-3d252328a144_1024x1536.png 424w, https://substackcdn.com/image/fetch/$s_!VG2v!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F798ce270-1cea-434f-b566-3d252328a144_1024x1536.png 848w, https://substackcdn.com/image/fetch/$s_!VG2v!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F798ce270-1cea-434f-b566-3d252328a144_1024x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!VG2v!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F798ce270-1cea-434f-b566-3d252328a144_1024x1536.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 role="img" 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><title></title><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><h3>Consensus in Distributed Computing</h3><p><em>Consensus </em>in its simplest form refers to <em>a general agreement</em>. In the weekend outing analogy, it refers to all friends agreeing to a location. Its quite likely that several options are considered before the group eventually agree on a particular location.</p><p>In distributed computing too, one or more nodes may propose <em>values. </em>Of all these values one of it need to be agreed upon by all the nodes. Its up to the consensus algorithm to <em>decide </em>upon one of these values and propagate the decision to all the nodes.</p><p>Formally, a consensus algorithm <em>must </em>satisfy below properties &#8594;</p><ol><li><p><em>Uniform agreement &#8594; </em>All the nodes <em>agree </em>upon the same value&#8202;&#8212;&#8202;even if the node itself has <em>proposed a different value </em>initially.</p></li><li><p><em>Integrity &#8594; </em>Once a value is <em>agreed </em>by the node, it shouldn&#8217;t change.</p></li><li><p><em>Validity &#8594; </em>If a node <em>agrees </em>to a value, it must have been proposed at least by one another node too.</p></li><li><p><em>Termination &#8594; </em>Eventually every participating node agrees upon a <em>value.</em></p></li></ol><p>The <em>uniform agreement</em> and <em>integrity </em>forms the core idea of consensus&#8202;&#8212;&#8202;everyone agree on <em>same value</em>, and once decided, its final.</p><p>The <em>validity </em>property ensures elimination of trivial behavior wherein a node agrees to a value irrespective of what has been proposed.</p><p>The <em>termination</em> property ensures fault tolerance. If one or more nodes fails the cluster should progress and eventually agree upon a value. This also eliminate the possibility of a <em>dictator</em> node which takes all decisions and jeopardies the whole cluster in case it fails.</p><p>Of course, if <em>all </em>the nodes fails the algorithm can&#8217;t proceed. There is a limit to number of failures a algorithm can tolerate. A algorithm that can correctly guarantee consensus amongst <em>n</em> nodes of which at most <em>t</em> fail is said to be <em>t-resilient</em>.</p><p>In essence termination property can be termed as <em>liveness </em>guarantee while rest three as <em>safety </em>guarantee.</p><h3>Raft</h3><p>Raft stands for <em><strong>Reliable, Replicated, Redundant, And Fault-Tolerant</strong></em>, reflecting its design principles in distributed systems. It ensures <em>reliability </em>by maintaining consistent logs, <em>replication </em>across nodes for durability, <em>redundancy</em> to avoid single points of failure, and <em>fault tolerance </em>to continue operating despite crashes or network issues. Together, these qualities make Raft a robust consensus algorithm for distributed computing.</p><h4>Explanation</h4><p><em>Raft </em>utilizes <em>leader </em>approach to achieve consensus. In a <em>Raft </em>cluster a node is either a <em>leader </em>or a <em>follower</em>. A node could also be a <em>candidate </em>for a brief duration when a leader is unavailable i.e. <a href="https://en.wikipedia.org/wiki/Leader_election">leader election</a> is underway.</p><p>The cluster has <em>one and only one elected leader</em> which is fully responsible for managing log replication on the other nodes of the cluster. It means that the leader can decide between it and the other nodes without consulting other nodes. A <em>leader </em>leads until it fails or disconnects, in which case remaining nodes elect a new leader.</p><p>Fundamentally thus the consensus problem is broken into two independent sub-problem in Raft as <em>Leader Election</em> and <em>Log Replication</em>.</p><p><strong>Leader Election</strong></p><p>Leader election in Raft occurs when the current leader fails or during initialization. Each election begins a new <em>term</em>, a time period in which a leader must be chosen. A node becomes a candidate if it doesn&#8217;t receive heart beats from a leader within the election timeout. It then increments the term, votes for itself, and requests votes from others. Nodes vote once per term, on first-come-first-served basis. A candidate wins if it secures a majority; otherwise, initiating a new term and election. Randomized timeouts reduce split votes by staggering candidate starts, ensuring quicker resolution and stable leadership through heartbeat messages.</p><blockquote><p>Raft is not <a href="https://en.wikipedia.org/wiki/Byzantine_fault">Byzantine fault tolerant</a>; the nodes trust the elected leader, and the algorithm assumes all participants are trustworthy.</p></blockquote><p><strong>Log Replication</strong></p><p>The leader manages client requests and ensures consistency across the cluster. Each request is appended to the leader&#8217;s log and sent to followers. If followers are unavailable, the leader retries until replication succeeds.</p><p>Once a majority of followers confirm replication, the entry is committed, applied to the leader&#8217;s own state, and considered durable. This also commits prior entries, which followers then apply to their own state, maintaining log consistency across cluster.</p><p>In case a leader crashes, inconsistencies may arise if some entries were not fully replicated. A new leader resolves this by reconciling logs. It identifies the last matching entry with each follower, deletes conflicting entries in their logs, and replaces them with its own. Thus ensuring consistency even after failures.</p><h4>Additional Considerations</h4><p>Raft algorithm has below additional consideration for a robust consensus algorithm for distributed computing.</p><p><strong>Safety Guarantee</strong></p><p>Raft ensure below safety guarantees &#8594;</p><ul><li><p><em>Election safety &#8594;</em> at most one leader can be elected in a given term.</p></li><li><p><em>Leader append-only &#8594;</em> a leader can only append new entries to its logs (<em>it can neither overwrite nor delete entries</em>).</p></li><li><p><em>Log matching &#8594; </em>if two logs contain an entry with the same index and term, then the logs are identical in all entries up through the given index.</p></li><li><p><em>Leader completeness &#8594;</em> if a log entry is committed in a given term then it will be present in the logs of the leaders since this term.</p></li><li><p><em>State safety &#8594;</em> if a node has applied a particular log entry to its state , then no other node may apply a different command for the same log.</p></li></ul><p><strong>Cluster Membership Changes</strong></p><p>Raft handles cluster membership changes using<em> <strong>joint consensus</strong></em>, a transitional phase where both <em>old </em>and <em>new </em>configurations overlap.</p><p>During this phase, log entries must be committed to both sets, leaders can come from either, and elections require majorities from <em>both</em>. Once new configuration is replicated to a majority of its nodes, the system fully transitions.</p><p>Raft also addresses below three challenges &#8594;</p><ol><li><p>New nodes without logs are excluded from majorities until caught up.</p></li><li><p>Leaders not in new configuration step down to followers.</p></li><li><p>Nodes still with old configuration that still recognize a leader ignore disruptive vote requests.</p></li></ol><p><strong>Log Compaction</strong></p><p>Log compaction in Raft works by nodes taking snapshots of committed log entries, storing them with the <em>last index and term</em>. Leaders send these snapshots to lagging nodes, which then discard their log entirely or truncate it up to the snapshot&#8217;s latest entry. This also ensures <em>durability </em>in Raft.</p><h4>Limitations of Raft</h4><p>Raft has its own limitations with <em>trades off scalability and flexibility </em>as compared to other consensus algorithms<em>.</em></p><ol><li><p><em>Leader Bottleneck</em> &#8594; Raft relies heavily on a single leader to coordinate log replication. If the leader fails, the system pauses until a new leader is elected, which can slow progress.</p></li><li><p><em>Scaling </em>&#8594; Raft doesn&#8217;t scale well to very large clusters&#8202;&#8212;&#8202;leader elections and log replication become slower and riskier as the number of nodes grows.</p></li><li><p><em>Network partitions</em> &#8594; this can cause temporary unavailability, since Raft prioritizes consistency over availability. An edge case exist where the elected leader is forced to resign and leadership switched between nodes continuously. Thus, forcing whole cluster to halt.</p></li></ol><div><hr></div><h4>Real World Production Usage of Raft</h4><ul><li><p><strong><a href="https://en.wikipedia.org/wiki/Etcd">Etcd</a></strong> uses Raft to manage a highly-available replicated log&#8202;&#8212;&#8202;utilized primarily in Kubernetes cluster for configuration management.</p></li><li><p><strong><a href="https://en.wikipedia.org/wiki/Neo4j">Neo4j</a> </strong>uses Raft to ensure consistency and safety.</p></li><li><p><strong>Apache Kafka Raft (KRaft)</strong> uses Raft for metadata management. In the recent version <a href="https://medium.com/@husain.ammar/breaking-free-from-zookeeper-why-kafkas-kraft-mode-matters-90819c3c3e57">KRaft replaced Apache Zookeeper</a> in Kafka.</p></li><li><p><strong><a href="https://en.wikipedia.org/wiki/Camunda">Camunda</a> </strong>uses the Raft consensus algorithm for data replication.</p></li></ul><div><hr></div><h3>Raft vs Paxos</h3><p>Raft was introduced to make consensus easier to understand and implement compared to Paxos. While Paxos is theoretically robust, it&#8217;s notoriously complex, making it hard for engineers to build reliable systems from it. Raft simplifies the process by breaking consensus into clear steps&#8202;&#8212;<em>&#8202;leader election, log replication, and safety&#8202;</em>&#8212;&#8202;without sacrificing correctness. This clarity makes Raft more approachable for real-world distributed systems.</p><h4><strong>When to Choose</strong></h4><ul><li><p><strong>Raft &#8594;</strong> Useful when building new distributed systems where clarity, maintainability, and developer adoption matter (<em>e.g., databases, coordination services</em>).</p></li><li><p><strong>Paxos &#8594;</strong> Useful in academic or highly specialized systems where <em>theoretical rigor</em> is prioritized over ease of implementation.</p></li></ul><p>In practice, Raft is usually the better choice for modern engineering teams because it balances correctness with simplicity.</p><div><hr></div><h4><strong>Future Trends in Consensus</strong></h4><p>Future consensus algorithms are moving beyond leader-based models like Raft and Paxos. A key trend is <strong><a href="https://gramoli.github.io/2026/02/13/leaderless-consensus.html">leaderless consensus</a></strong>, where no single node coordinates decisions. Instead, all nodes collaborate equally, reducing the risk of a single point of failure. This makes systems more resilient and fair, especially in global networks where reliability is critical. For example, in blockchain or distributed databases, leaderless designs help ensure trust and consistency without relying on one &#8220;<em>boss</em>&#8221; node.</p><p>Another trend is <strong>scalability-focused consensus</strong>, which aims to cut down communication overhead. As systems grow to thousands of nodes, traditional methods struggle with efficiency. New protocols are exploring ways to minimize message exchanges while still guaranteeing agreement.</p><p>Also <strong>hybrid approaches </strong>are explored combining leaderless designs with probabilistic or quorum-based methods. These balance speed and fault tolerance, making them suitable for high-performance applications.</p><p>Finally, <strong>energy-efficient consensus</strong> is gaining attention, especially in blockchain, where <em>proof-of-work</em> is costly. Future algorithms will likely emphasize greener, lightweight mechanisms.</p><p>Consensus is evolving toward <em>fairness, scalability, and sustainability</em>&#8202;&#8212;&#8202;ensuring distributed systems can <em>handle global scale without sacrificing reliability</em>.</p><div><hr></div><h3>Conclusion</h3><p>Raft simplifies the complex world of distributed consensus by breaking it into clear steps&#8202;&#8212;&#8202;<em>leader election, log replication, and safety guarantees</em>. While engineers may not encounter Raft every day, understanding it is essential when making architectural or design decisions for systems that demand reliability and consistency.</p><p>Raft ensures that clusters agree on shared state even in the face of failures, though it comes with <em>trade&#8209;offs like leader bottlenecks and limited scalability</em>.</p><p>Its adoption in tools such as <em>etcd, Kafka, and Neo4j</em> shows its practical importance. Compared to Paxos, Raft is easier to grasp and implement, making it a strong foundation for modern distributed systems.</p><p>As consensus evolves toward leaderless and scalable designs, Raft remains a critical concept every architect should be aware of when shaping resilient, fault&#8209;tolerant solutions.</p><div><hr></div><h4>References and Further Read</h4><ul><li><p><a href="https://en.wikipedia.org/wiki/Consensus_%28computer_science%29">Consensus</a></p></li><li><p><a href="https://en.wikipedia.org/wiki/Raft_%28algorithm%29">Raft Algorithm</a></p></li><li><p><a href="https://raft.github.io/">Raft (GitHub)</a></p></li><li><p><a href="https://dataintensive.net/">Designing Data-Intensive Applications</a></p></li><li><p><a href="https://martinfowler.com/books/patterns-distributed.html">Patterns of Distributed Systems</a></p></li></ul><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://ammarhusain.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">Thanks for reading! Subscribe for free to receive new posts and support my work.</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[From Chaos to Order: Understanding Structured Concurrency in Java]]></title><description><![CDATA[Java&#8217;s Path to Concurrent Efficiency]]></description><link>https://ammarhusain.substack.com/p/from-chaos-to-order-understanding</link><guid isPermaLink="false">https://ammarhusain.substack.com/p/from-chaos-to-order-understanding</guid><dc:creator><![CDATA[Ammar Husain]]></dc:creator><pubDate>Mon, 26 Jan 2026 06:46:18 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!DzuX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ce8091d-5e48-4729-b910-9fa8bc8eea3b_1024x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2><strong>Introduction</strong></h2><p>Typically, complexity in programming is managed by breaking down tasks into sub-tasks. These sub-tasks can then be executed concurrently.</p><p>Since Java 5, <code>ExecutorService</code> API helps programmer to execute these sub-tasks concurrently. However, given the nature of concurrent execution each of sub-task could fail or succeed independently with no implicit communication between themselves. The failure in one sub-task does not automatically cancel the other sub-tasks<em>. </em>Although an attempt can be made to manage these cancellation manually via external handling, its quite tricky to get right &#8212; especially when large number of sub-tasks are involved. This could potentially result in <em>loose threads</em> (alternatively know as <em>thread leaks). </em>Although <em>Virtual Threads </em>is a cost-effective way to dedicate a thread to each (sub)task, managing the results from<em> </em>them still remains a challenge.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://ammarhusain.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">Thanks for reading! Subscribe for free to receive new posts and support my work.</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><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!DzuX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ce8091d-5e48-4729-b910-9fa8bc8eea3b_1024x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!DzuX!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ce8091d-5e48-4729-b910-9fa8bc8eea3b_1024x1024.png 424w, https://substackcdn.com/image/fetch/$s_!DzuX!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ce8091d-5e48-4729-b910-9fa8bc8eea3b_1024x1024.png 848w, https://substackcdn.com/image/fetch/$s_!DzuX!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ce8091d-5e48-4729-b910-9fa8bc8eea3b_1024x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!DzuX!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ce8091d-5e48-4729-b910-9fa8bc8eea3b_1024x1024.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!DzuX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ce8091d-5e48-4729-b910-9fa8bc8eea3b_1024x1024.png" width="1024" height="1024" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3ce8091d-5e48-4729-b910-9fa8bc8eea3b_1024x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1024,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:306228,&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://ammarhusain.substack.com/i/185812920?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ce8091d-5e48-4729-b910-9fa8bc8eea3b_1024x1024.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!DzuX!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ce8091d-5e48-4729-b910-9fa8bc8eea3b_1024x1024.png 424w, https://substackcdn.com/image/fetch/$s_!DzuX!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ce8091d-5e48-4729-b910-9fa8bc8eea3b_1024x1024.png 848w, https://substackcdn.com/image/fetch/$s_!DzuX!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ce8091d-5e48-4729-b910-9fa8bc8eea3b_1024x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!DzuX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3ce8091d-5e48-4729-b910-9fa8bc8eea3b_1024x1024.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 role="img" 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><title></title><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>Executor service allows one thread to create it and another thread to submit its tasks to this executor service. The threads which performs the tasks bear no relation to both of these threads. Moreover, a completely different thread can await on the result of execution from these sub-tasks via reference to its <code>Future</code> object &#8212; which is provided immediately upon task submission to executor service. Thus, effectively the sub-task does not have to return to the task that submitted it. It could possibly return to multiple threads or none.</p><p>Also, the relation between task &amp; its sub-task is only logical and not visible in the code structure. There is no enforcement or tracking of task to sub-tasks relationship in runtime as well.</p><p><em><strong>Structured Concurrency</strong></em> in Java aims to solve all of the above challenges by &#8594;</p><ul><li><p>reliably automating the cancellation of sub-tasks; avoiding the thread leaks and delays.</p></li><li><p>ensuring a (sub)task return only to the thread which submitted it.</p></li><li><p>enforcing a structured relation between task and its sub-tasks &#8212; which could be nested as well.</p></li></ul><h2><strong>Unstructured Concurrency</strong></h2><p>Lets understand more about current situation of unstructured concurrency in Java. Consider a function <code>handle()</code>which fetch user information and its associated order.</p><pre><code>Response handle() throws ExecutionException, InterruptedException {
    Future&lt;String&gt;  user  = esvc.submit(() -&gt; findUser());
    Future&lt;Integer&gt; order = esvc.submit(() -&gt; fetchOrder());
    String theUser  = user.get();   // Join findUser
    int    theOrder = order.get();  // Join fetchOrder
    return new Response(theUser, theOrder);
}</code></pre><p>At first glance the code seems simple which does what it intends to do. However, on closer look we could identify multiple issues &#8594;</p><ul><li><p>If <code>findUser()</code> throws exception then the thread running <code>fetchOrder()</code> leaks as latter has no information or knowledge about former&#8217;s execution status.</p></li><li><p>If thread running <code>handle()</code>is interrupted then both the threads running <code>findUser()</code><em> </em>and <code>fetchOrder()</code> leaks as these threads too have no knowledge of the thread which spawned them.</p></li><li><p>If <code>findUser()</code>took too long and meanwhile<em> </em><code>fetchOrder()</code><em> </em>fails, the failure would only be identified when <code>order.get()</code><em> </em>is invoked.</p></li><li><p>Although the code is structured as task related to its sub-task, this relation is mere logical and is neither explicitly described at compile time nor enforced during runtime.</p></li></ul><p>The first three situation arise due to lack of any automated mechanism for cancelling the other threads. This potentially leads to wastage of resources (<em>as threads continue to execute</em>), cancellation delays and at worst these leaked threads may interfere with other threads too. Although we may attempt to handle the cancellation manually, not only its tricky to get them right it complicates the overall program and create more room for errors.</p><p>The fourth situation arises due to lack of formal syntax which binds the threads into a parent-child relationship for their task to sub-task hierarchy. The <code>Future</code> object provided are unhelpful too in this case.</p><p>All these limitations are due to <em>unstructured </em>nature of concurrency via <code>ExecutorService</code> and <code>Future</code> which lacks in providing an automated way of cancellation or track task to sub-task relationships.</p><h2><strong>Structured Concurrency</strong></h2><p>Structured concurrency principles that</p><blockquote><p><em>If a task splits into concurrent sub-tasks then they all return to the same place, namely the task&#8217;s code block.</em></p></blockquote><p>In structured concurrency, the task awaits the sub-tasks&#8217; results and monitors them for failures. Also using the APIs a well-defined entry and exit points for the flow of execution through a block of code is defined. APIs also help in enforcing a strict nesting of the lifetimes of operations in a way that mirrors their syntactic nesting in the code.</p><p>Structured concurrency has a natural synergy with virtual threads. A new virtual thread can be dedicated to every task, and when a task fans out by submitting sub-tasks for concurrent execution then it can dedicate a new virtual thread to each sub-task too. Moreover, the task-subtask relationship has a tree structure for each virtual thread to carry a reference to its unique parent.</p><p>While virtual threads deliver an abundance of threads, structured concurrency can correctly and robustly coordinate them. This also enables observability tools to display threads as they are understood by the developer.</p><h3><strong>StructuredTaskScope</strong></h3><p>In Structured Concurrency API, <code>StructuredTaskScope</code>is the principal class.</p><p>The earlier example of <code>handle()</code><em> </em>function rewritten with <code>StructuredTaskScope</code> is shown below</p><pre><code>Response handle() throws InterruptedException {
    try (var scope = StructuredTaskScope.open()) {
        Subtask&lt;String&gt; user = scope.fork(() -&gt; findUser());
        Subtask&lt;Integer&gt; order = scope.fork(() -&gt; fetchOrder());
        scope.join();   // Join subtasks, propagating exceptions
        // Here both subtasks have succeeded, so compose their results
        return new Response(user.get(), order.get());
    }
}</code></pre><p>With use of these APIs we achieve all of the below, which addresses the shortcomings of the unstructured concurrency discussed so far.</p><ul><li><p>On failure of either <code>findUser()</code><em> </em>or <code>fetchOrder()</code> other is automatically cancelled; in case not completed yet.</p></li><li><p>In case the thread running <code>handle()</code>is interrupted before or during invocation to <code>join()</code>both the sub-task viz. <code>findUser()</code> or <code>fetchOrder()</code><em> </em>are cancelled; in case not completed yet.</p></li><li><p>The task structure and code mirror each other with abundant clarity. The <code>scope</code> is considered task (<em>parent</em>) while the <code>fork</code> are sub-tasks (<em>children</em>). The task waits for sub-tasks to complete or be cancelled and decide to succeed or fail with no overhead of lifecycle management.</p></li><li><p>An additional benefit earned via the hierarchy of task to sub-task is major improvement in observability. The call stack or thread dump clearly displays the relationship between the <code>handle()</code><em> </em>to <code>findUser()</code> and <code>fetchOrder()</code><em> </em>which can easily be understood by developer.</p></li></ul><p>With the automatic cancellation/cleanup achieved with default <em>completion policy</em>, via zero-parameter <code>open</code> factory method, thread leaks are avoided altogether. The default completion policy is &#8212; <em>fail whole scope if any sub-task fails</em>. This policy can be customized or replaced with other available suitable policy too with <code>StructuredTaskScope.Joiner</code>&#8212; refer <em><strong>Completion Policies</strong></em> section later in this article for more details.</p><p>Before proceeding lets review few of the important characteristics of <code>StructuredTaskScope</code> a developer should be aware of &#8594;</p><ol><li><p>As of <em><strong>JDK 26</strong></em> this is still a preview feature and thus disabled by default.</p></li><li><p>The thread that creates the scope is its <em>owner.</em></p></li><li><p>With every invocation of <code>fork(&#8230;)</code> new virtual thread is started by default &#8212; for execution of respective sub-task.</p></li><li><p>A sub-task can create its own nested <code>StructuredTaskScope</code> to fork its own sub-tasks, thus creating a hierarchy. Once the scope is <em>closed </em>all of its sub-tasks are guaranteed to be terminated ensuring no threads are leaked.</p></li><li><p>If <code>fork()</code>is invoked by thread which either is non-owner or not part of scope hierarchy, <code>WrongThreadException</code> is thrown.</p></li><li><p>Calling <code>join()</code> within a scope is <em>mandatory</em>. If a scope&#8217;s block exits before joining then the scope will wait for all sub-tasks to terminate and then throw an exception.</p></li><li><p>When <code>join()</code> completes successfully then each of the sub-tasks has either completed successfully, failed, or been cancelled because the scope was closed.</p></li><li><p><code>StructuredTaskScope</code> enforces structure and order upon concurrent operations. Thus, using a scope outside of a <code>try-with-resources</code> block and returning without calling <code>close()</code>, or without maintaining the proper nesting of <code>close()</code> calls, may cause the scope&#8217;s methods to throw a <code>StructureViolationException</code>.</p></li><li><p>To allow for efficient cancellation, sub-tasks must be coded so that they finish as soon as possible when <em>interrupted</em>. Sub-tasks that do not respond to interrupts because, e.g., they block on methods that are not interruptible, may delay the closing of a scope <em>indefinitely</em>. The <code>close</code> method always waits for threads executing sub-tasks to finish, even if the scope is cancelled.</p></li><li><p>Sub-tasks forked in a scope inherit <code>ScopedValue</code> bindings. If a scope&#8217;s owner reads a value from a bound <code>ScopedValue</code> then each sub-task will read the same value. More details about the scoped values can be read <a href="https://medium.com/@husain.ammar/scoped-values-revolutionizing-java-context-management-8d9b5e3e0b2e">here</a>.</p></li></ol><h3><strong>Completion Policies</strong></h3><p>To avoid any unnecessary processing during concurrent sub-task execution it is common to use <em>short-circuiting patterns. </em>In Structured Concurrency completion policies via <em>out-of-box </em>or<em> custom </em><code>StructuredTaskScope.Joiner</code> can be utilized for specific requirements.</p><p>For common use cases below factory methods are provided &#8594;</p><ul><li><p><code>allSuccessfulOrThrow()</code> &#8594; Use it when results from all sub-tasks are of <em>same type</em> and are required for overall processing. In case any sub-task fails, it cancels the scope &amp; remaining sub-task(s) and causes <code>join</code> to throw.</p></li><li><p><code>anySuccessfulOrThrow()</code> &#8594; Use it if any one of the sub-task result is suffice for overall processing. As soon as the result of the first successful sub-task is available, <code>join</code> returns with the value. This policy causes <code>join</code> to throw <em>only </em>if all sub-tasks fail.</p></li><li><p><code>awaitAllSuccessfulOrThrow()</code> &#8594; <em>Default </em>policy employed with <code>StructuredTaskScope.open</code> . Use it when results from all sub-tasks are required for overall processing. It is suited when the sub-task result are of different type, in contrast to <code>allSuccessfulOrThrow()</code> where result of sub-task are of same type. Here too in case any sub-task fails it cancels the scope, &amp; remaining sub-task(s), and causes <code>join</code> to throw.</p></li><li><p><code>awaitAll()</code> &#8594; This Joiner is useful for cases where sub-tasks make use of <em>side-effects</em> rather than return results or fail with exceptions. This <code>Joiner</code> can also be used for <em>fan-in</em> scenarios where sub-tasks are forked to handle incoming connections and the number of sub-tasks is unbounded. <em>Note </em>&#8212; The <code>Joiner</code> does <em>not </em>cancel the scope if a sub-task <em>fails</em>.</p></li></ul><p>The earlier example of <code>handle</code> already shows the implicit usage of <code>awaitAllSuccessfulOrThrow()</code> as <em>default completion policy </em>via <code>open</code> . Below example exhibits usage of <code>anySuccessfulOrThrow()</code> which returns the result of first successful sub-task. A real world example would be to query from multiple servers for a response and return result of the very first response from any of the server (<em>tail chopping</em>).</p><pre><code>&lt;T&gt; T race(Collection&lt;Callable&lt;T&gt;&gt; tasks) throws InterruptedException {
    try (var scope = StructuredTaskScope.open(Joiner.&lt;T&gt;anySuccessfulOrThrow())) {
        tasks.forEach(scope::fork);
        return scope.join();
    }
}</code></pre><p>Note that as soon as one sub-task succeed this scope automatically shuts down, cancelling unfinished sub-tasks. The scope fails only if all of the sub-tasks fail.</p><p>In below example when all sub-tasks complete successfully, a stream of the sub-tasks results are returned as per the utilized policy of <code>allSuccessfulOrThrow</code> .</p><pre><code>&lt;T&gt; List&lt;T&gt; runConcurrently(Collection&lt;Callable&lt;T&gt;&gt; tasks) throws InterruptedException {
    try (var scope = StructuredTaskScope.open(Joiner.&lt;T&gt;allSuccessfulOrThrow())) {
        tasks.forEach(scope::fork);
        return scope.join().map(Subtask::get).toList();
    }
}</code></pre><p>Here, if one or more sub-tasks fail then <code>join()</code> throws a <code>FailedException</code> &#8212; with the exception from one of the failed sub-tasks as its cause. The <code>join()</code> method returns a stream of completed sub-tasks rather than <code>null</code>, making this joiner suited for cases where all sub-tasks return a result of the same type and where the <code>Subtask</code> objects returned by the <code>fork</code> method is ignored.</p><p><strong>Custom Completion Policies</strong></p><p>The <code>Joiner</code> interface can be implemented directly in order to support <em>custom completion policies</em>. It has two type parameters: <code>T</code> for the result type of the subtasks executed in the scope, and <code>R</code> for the result type of the <code>join()</code> method. The key methods to be implemented are <code>onFork</code> , <code>onComplete</code> and <code>result</code> .</p><p>In below custom <code>Joiner</code> class the results of sub-tasks that complete successfully are collected, ignoring sub-tasks that fail. The <code>onComplete</code> method may be invoked by several threads concurrently, and so must be thread-safe. The <code>result</code> method returns a stream of the task results.</p><pre><code>class CollectingJoiner&lt;T&gt; implements Joiner&lt;T, Stream&lt;T&gt;&gt; {
    private final Queue&lt;T&gt; results = new ConcurrentLinkedQueue&lt;&gt;();
    public boolean onComplete(Subtask&lt;? extends T&gt; subtask) {
        if (subtask.state() == Subtask.State.SUCCESS) {
            results.add(subtask.get());
        }
        return false;
    }
    public Stream&lt;T&gt; result() {
        return results.stream();
    }
}</code></pre><p>This custom policy can then be used as below &#8594;</p><pre><code>&lt;T&gt; List&lt;T&gt; allSuccessful(List&lt;Callable&lt;T&gt;&gt; tasks) throws InterruptedException {
      try (var scope = StructuredTaskScope.open(new CollectingJoiner&lt;T&gt;())) {
          tasks.forEach(scope::fork);
          return scope.join().toList();
      }
  }</code></pre><h3><strong>Exception Handling</strong></h3><p>When the scope is considered to have failed, the <code>join()</code> method throws a <code>FailedException</code> which wraps the underlying cause from the sub-task.</p><p>In the <code>handle()</code> example, it may be useful to add a <code>catch</code> block to the <code>try-with-resources</code> statement in order to handle exceptions after the scope is closed as shown below &#8594;</p><pre><code>try (var scope = StructuredTaskScope.open()) {
   ...
} catch (StructuredTaskScope.FailedException e) {
   Throwable cause = e.getCause();
   switch (cause) {
       case IOException ioe -&gt; ..
       default -&gt; ..
   }
}</code></pre><h3><strong>Configurations</strong></h3><p>The <code>StructuredTaskScope</code> API has an additional overloaded <code>open</code> method which accepts a <code>Joiner</code> together with a function which can produce a <a href="https://download.java.net/java/early_access/loom/docs/api/java.base/java/util/concurrent/StructuredTaskScope.Config.html">configuration object</a>. This can help set the scope&#8217;s name for monitoring and management purposes, set the scope&#8217;s timeout, and to set the <a href="https://docs.oracle.com/en/java/javase/24/docs/api/java.base/java/util/concurrent/ThreadFactory.html">thread factory</a> to be used by the scope&#8217;s <code>fork</code> methods to create threads.</p><p>Below is a revised version of the <code>runConcurrently</code> method which sets a thread factory and a timeout &#8594;</p><pre><code>&lt;T&gt; List&lt;T&gt; runConcurrently(Collection&lt;Callable&lt;T&gt;&gt; tasks,
                            ThreadFactory factory,
                            Duration timeout)
    throws InterruptedException
{
    try (var scope = StructuredTaskScope.open(Joiner.&lt;T&gt;allSuccessfulOrThrow(),
                                              cf -&gt; cf.withThreadFactory(factory)
                                                      .withTimeout(timeout))) {
        tasks.forEach(scope::fork);
        return scope.join().map(Subtask::get).toList();
    }
}</code></pre><p>If the timeout expires before or while waiting in the <code>join()</code> method then the scope is cancelled, which cancels all incomplete sub-tasks, and <code>join()</code> throws a <code>TimeoutException</code>.</p><h2><strong>Conclusion</strong></h2><p><em><strong>Structured Concurrency</strong></em> in Java represents a significant evolution in concurrent programming, addressing many of the shortcomings found in <em>traditional unstructured concurrency</em> models.</p><p>Key advantages of structured concurrency in Java includes &#8212;</p><ul><li><p><strong>Automated Cancellation &#8212;</strong> Sub-tasks are automatically cancelled upon failure, reducing resource wastage and eliminating the complexity of manual cancellation handling.</p></li><li><p><strong>Clear Task Hierarchy &#8212;</strong> The hierarchical task structure enhances code readability, maintainability, and observability, making it easier to debug and monitor concurrent operations.</p></li><li><p><strong>Improved Error Handling &#8212;</strong> Centralized error handling through structured concurrency ensures predictable and robust behavior in the presence of exceptions.</p></li><li><p><strong>Enhanced Observability &#8212; </strong>The clear parent-child relationships displayed in thread dumps and call stacks aid developers in understanding and managing concurrent tasks.</p></li><li><p><strong>Virtual Threads &#8212;</strong> The synergy with virtual threads allows for efficient and scalable concurrent programming, making it possible to handle a large number of concurrent tasks without the overhead of traditional threads.</p></li></ul><p>By adopting structured concurrency, Java developers can write more efficient, reliable, and maintainable concurrent code, ultimately leading to better software quality and improved developer productivity.</p><h3><strong>References and Further Reads</strong></h3><ul><li><p><a href="https://en.wikipedia.org/wiki/Structured_concurrency">Structured Concurrency</a></p></li><li><p><a href="https://docs.oracle.com/en/java/javase/25/core/structured-concurrency.html">Oracle Docs</a></p></li><li><p><a href="https://openjdk.org/jeps/525">JEP 525</a></p></li></ul><h2><strong>Structured Concurrency in Various Programming Languages</strong></h2><p>While Structured Concurrency is still being previewed in Java, it is already available in various programming languages. Here is sneak peak for few of these languages.</p><p><strong>Kotlin</strong></p><ul><li><p><strong>Coroutines &#8212;</strong> Kotlin offers coroutines, which are a lightweight concurrency model that allows for asynchronous programming without blocking threads. Coroutines provide structured concurrency through scopes, ensuring that tasks are properly managed and cancelled when necessary.</p></li><li><p><strong>Structured Concurrency &#8212; </strong>Kotlin&#8217;s structured concurrency is built into its coroutine framework, making it easy to write concurrent code that is both efficient and easy to understand.</p></li></ul><p><strong>Go</strong></p><ul><li><p><strong>Goroutines &#8212;</strong> Go uses goroutines, which are lightweight threads managed by the Go runtime. Goroutines can be easily created and managed, allowing for high concurrency.</p></li><li><p><strong>Channels &#8212;</strong> Go provides channels for communication between goroutines, enabling structured concurrency by ensuring that tasks can communicate and synchronize effectively.</p></li></ul><p><strong>Python:</strong></p><ul><li><p><strong>Asyncio &#8212;</strong> Python&#8217;s asyncio library provides support for asynchronous programming using coroutines. Asyncio allows for structured concurrency through the use of tasks and event loops, ensuring that tasks are properly managed and cancelled when necessary.</p></li><li><p><strong>Task Groups &#8212;</strong> Python&#8217;s asyncio library includes task groups, which provide a way to manage and cancel groups of tasks together, ensuring that tasks are properly coordinated.</p></li></ul><p><strong>C#</strong></p><ul><li><p><strong>Async/Await &#8212;</strong> C# provides support for asynchronous programming using the async and await keywords. This allows for structured concurrency by ensuring that tasks are properly managed and cancelled when necessary.</p></li><li><p><strong>Task Parallel Library (TPL) &#8212; </strong>The TPL provides support for parallel programming in C#, allowing for the creation and management of tasks in a structured manner.</p></li></ul><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://ammarhusain.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">Thanks for reading! Subscribe for free to receive new posts and support my work.</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></channel></rss>