<?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[The Java Knowledge Ocean: System Design]]></title><description><![CDATA[Introduction to the principles and best practices of system design.]]></description><link>https://seabreeze0828.substack.com/s/system-design</link><image><url>https://substackcdn.com/image/fetch/$s_!MgS-!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2006b880-951e-4af1-8243-52e5cdef4c73_676x676.png</url><title>The Java Knowledge Ocean: System Design</title><link>https://seabreeze0828.substack.com/s/system-design</link></image><generator>Substack</generator><lastBuildDate>Wed, 19 Aug 2026 13:50:06 GMT</lastBuildDate><atom:link href="https://seabreeze0828.substack.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Dylan Smith]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[dylan0828@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[dylan0828@substack.com]]></itunes:email><itunes:name><![CDATA[Dylan Smith]]></itunes:name></itunes:owner><itunes:author><![CDATA[Dylan Smith]]></itunes:author><googleplay:owner><![CDATA[dylan0828@substack.com]]></googleplay:owner><googleplay:email><![CDATA[dylan0828@substack.com]]></googleplay:email><googleplay:author><![CDATA[Dylan Smith]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[The Most Comprehensive Explanation of Session, Cookie, Token, and JWT on the Entire Network!🔥]]></title><description><![CDATA[Understanding HTTP statelessness, Cookie security flags, Session limitations in distributed systems, and the mechanics of Access/Refresh Tokens]]></description><link>https://seabreeze0828.substack.com/p/the-most-comprehensive-explanation</link><guid isPermaLink="false">https://seabreeze0828.substack.com/p/the-most-comprehensive-explanation</guid><dc:creator><![CDATA[Dylan Smith]]></dc:creator><pubDate>Sat, 15 Aug 2026 16:09:06 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!sWe9!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F908e6efa-5d5c-489f-817d-885531dd587c_1313x787.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!sWe9!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F908e6efa-5d5c-489f-817d-885531dd587c_1313x787.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!sWe9!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F908e6efa-5d5c-489f-817d-885531dd587c_1313x787.png 424w, https://substackcdn.com/image/fetch/$s_!sWe9!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F908e6efa-5d5c-489f-817d-885531dd587c_1313x787.png 848w, https://substackcdn.com/image/fetch/$s_!sWe9!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F908e6efa-5d5c-489f-817d-885531dd587c_1313x787.png 1272w, https://substackcdn.com/image/fetch/$s_!sWe9!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F908e6efa-5d5c-489f-817d-885531dd587c_1313x787.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!sWe9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F908e6efa-5d5c-489f-817d-885531dd587c_1313x787.png" width="1313" height="787" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/908e6efa-5d5c-489f-817d-885531dd587c_1313x787.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:787,&quot;width&quot;:1313,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!sWe9!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F908e6efa-5d5c-489f-817d-885531dd587c_1313x787.png 424w, https://substackcdn.com/image/fetch/$s_!sWe9!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F908e6efa-5d5c-489f-817d-885531dd587c_1313x787.png 848w, https://substackcdn.com/image/fetch/$s_!sWe9!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F908e6efa-5d5c-489f-817d-885531dd587c_1313x787.png 1272w, https://substackcdn.com/image/fetch/$s_!sWe9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F908e6efa-5d5c-489f-817d-885531dd587c_1313x787.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://seabreeze0828.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">The Java Knowledge Ocean is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>The HTTP protocol is a &#8220;<strong>stateless protocol</strong>&#8221;, that is, every time the server receives a request from the client, it is a completely new request, and the server does not know the historical request records of the client. The main purpose of Session and Cookie is to make up for the stateless nature of HTTP.</p><h2><strong>What is Session?</strong></h2><p>When the client requests the server, the server will open up a &#8220;memory space&#8221; for this request. The memory space stores the Session object, and the storage structure is <code>ConcurrentHashMap</code>. Session makes up for the stateless nature of HTTP. The server can use Session to store some operation records of the client during the same session.</p><h3><strong>How to determine whether it is the same session?</strong></h3><p>Because when the server receives the request for the first time, it opens up a Session space (creates a Session object), and at the same time generates a sessionId, and sends a response to the client requesting to set a Cookie through the <code>Set-Cookie: JSESSIONID=XXX</code> command in the response header.</p><p>After the client receives the response, it sets a Cookie information of <code>JSESSIONID=XXX</code> on the local client. The expiration time of this Cookie is the end of the browser session.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!8YTP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0896d33-879a-4253-90ac-a89b011ad00f_1313x454.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!8YTP!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0896d33-879a-4253-90ac-a89b011ad00f_1313x454.png 424w, https://substackcdn.com/image/fetch/$s_!8YTP!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0896d33-879a-4253-90ac-a89b011ad00f_1313x454.png 848w, https://substackcdn.com/image/fetch/$s_!8YTP!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0896d33-879a-4253-90ac-a89b011ad00f_1313x454.png 1272w, https://substackcdn.com/image/fetch/$s_!8YTP!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0896d33-879a-4253-90ac-a89b011ad00f_1313x454.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!8YTP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0896d33-879a-4253-90ac-a89b011ad00f_1313x454.png" width="1313" height="454" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a0896d33-879a-4253-90ac-a89b011ad00f_1313x454.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:454,&quot;width&quot;:1313,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!8YTP!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0896d33-879a-4253-90ac-a89b011ad00f_1313x454.png 424w, https://substackcdn.com/image/fetch/$s_!8YTP!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0896d33-879a-4253-90ac-a89b011ad00f_1313x454.png 848w, https://substackcdn.com/image/fetch/$s_!8YTP!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0896d33-879a-4253-90ac-a89b011ad00f_1313x454.png 1272w, https://substackcdn.com/image/fetch/$s_!8YTP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa0896d33-879a-4253-90ac-a89b011ad00f_1313x454.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Next time when the client sends a request to the same website each time, the request header will carry this Cookie information (including sessionId). Then, by reading the Cookie information in the request header, the server obtains the value named JSESSIONID and gets the sessionId of this request.</p><h3><strong>Disadvantages of Session</strong></h3><p>However, the Session mechanism has a disadvantage. If your server does load balancing and stores the Session on server A during the first request. Suppose the traffic to server A surges within a period of time and requests will be forwarded to server B for access. But server B does not store the Session of server A, which will lead to the invalidation of the Session.</p><h2><strong>What is Cookie?</strong></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ytKE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc075b97-ed41-4050-b839-aa216bb3dba3_1313x875.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ytKE!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc075b97-ed41-4050-b839-aa216bb3dba3_1313x875.jpeg 424w, https://substackcdn.com/image/fetch/$s_!ytKE!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc075b97-ed41-4050-b839-aa216bb3dba3_1313x875.jpeg 848w, https://substackcdn.com/image/fetch/$s_!ytKE!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc075b97-ed41-4050-b839-aa216bb3dba3_1313x875.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!ytKE!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc075b97-ed41-4050-b839-aa216bb3dba3_1313x875.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ytKE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc075b97-ed41-4050-b839-aa216bb3dba3_1313x875.jpeg" width="1313" height="875" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fc075b97-ed41-4050-b839-aa216bb3dba3_1313x875.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:875,&quot;width&quot;:1313,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!ytKE!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc075b97-ed41-4050-b839-aa216bb3dba3_1313x875.jpeg 424w, https://substackcdn.com/image/fetch/$s_!ytKE!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc075b97-ed41-4050-b839-aa216bb3dba3_1313x875.jpeg 848w, https://substackcdn.com/image/fetch/$s_!ytKE!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc075b97-ed41-4050-b839-aa216bb3dba3_1313x875.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!ytKE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffc075b97-ed41-4050-b839-aa216bb3dba3_1313x875.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Photo by <a href="https://unsplash.com/@peter_mc_greats?utm_source=medium&amp;utm_medium=referral">Pietro De Grandi</a> on <a href="https://unsplash.com/?utm_source=medium&amp;utm_medium=referral">Unsplash</a></p><p>You should have noticed that when introducing Session, Cookie has already been mentioned. Session is implemented based on cookie. Session is stored on the server side, and sessionId will be stored in the Cookie of the client.</p><p>Cookies in the HTTP protocol include <code>Web Cookie</code> and <code>browser Cookie</code>. It is a small piece of data sent by the server to the web browser. The Cookie sent by the server to the browser will be stored by the browser and sent to the server together with the next request. Usually, it is used to determine whether two requests come from the same browser, such as when a user remains logged in.</p><p>Cookies are mainly used for the following three purposes:</p><p><em><strong>1. Session management</strong></em></p><ul><li><p>Cooperate with the server and identify user sessions by storing <code>sessionid</code>.</p></li></ul><p><em><strong>2. Store user information</strong></em></p><ul><li><p>Login status: Remember whether the user is logged in. No need to log in again on the next visit.</p></li><li><p>Preference settings: Such as language, theme, etc. Automatically apply on the next visit.</p></li></ul><p><em><strong>3. Track user behavior</strong></em></p><ul><li><p>Browsing history: Record visited pages for easy recommendation and navigation.</p></li><li><p>Analyze behavior: Understand user habits for optimization and precision marketing.</p></li></ul><h3><strong>Creating Cookies</strong></h3><p>When receiving an HTTP request from a client, the server can send a response with the <code>Set-Cookie</code> header. Cookies are usually stored by the browser and then the Cookie is sent to the server along with the HTTP header.</p><h3><strong>Set-Cookie and Cookie headers</strong></h3><p>The <code>Set-Cookie</code> HTTP response header sends cookies from the server to the user agent. Here is an example of sending a Cookie.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!8sfC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a2244c6-a209-4bcc-b5f7-f6eb41ee506d_1313x435.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!8sfC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a2244c6-a209-4bcc-b5f7-f6eb41ee506d_1313x435.png 424w, https://substackcdn.com/image/fetch/$s_!8sfC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a2244c6-a209-4bcc-b5f7-f6eb41ee506d_1313x435.png 848w, https://substackcdn.com/image/fetch/$s_!8sfC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a2244c6-a209-4bcc-b5f7-f6eb41ee506d_1313x435.png 1272w, https://substackcdn.com/image/fetch/$s_!8sfC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a2244c6-a209-4bcc-b5f7-f6eb41ee506d_1313x435.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!8sfC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a2244c6-a209-4bcc-b5f7-f6eb41ee506d_1313x435.png" width="1313" height="435" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3a2244c6-a209-4bcc-b5f7-f6eb41ee506d_1313x435.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:435,&quot;width&quot;:1313,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!8sfC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a2244c6-a209-4bcc-b5f7-f6eb41ee506d_1313x435.png 424w, https://substackcdn.com/image/fetch/$s_!8sfC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a2244c6-a209-4bcc-b5f7-f6eb41ee506d_1313x435.png 848w, https://substackcdn.com/image/fetch/$s_!8sfC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a2244c6-a209-4bcc-b5f7-f6eb41ee506d_1313x435.png 1272w, https://substackcdn.com/image/fetch/$s_!8sfC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3a2244c6-a209-4bcc-b5f7-f6eb41ee506d_1313x435.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>This header tells the client to store cookies.</p><p>Now, with each new request to the server, the browser will send all previously stored cookies back to the server using the Cookie header.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!atiP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f19b758-1361-4b0d-adb6-90b0ea71a225_1313x800.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!atiP!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f19b758-1361-4b0d-adb6-90b0ea71a225_1313x800.png 424w, https://substackcdn.com/image/fetch/$s_!atiP!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f19b758-1361-4b0d-adb6-90b0ea71a225_1313x800.png 848w, https://substackcdn.com/image/fetch/$s_!atiP!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f19b758-1361-4b0d-adb6-90b0ea71a225_1313x800.png 1272w, https://substackcdn.com/image/fetch/$s_!atiP!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f19b758-1361-4b0d-adb6-90b0ea71a225_1313x800.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!atiP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f19b758-1361-4b0d-adb6-90b0ea71a225_1313x800.png" width="1313" height="800" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6f19b758-1361-4b0d-adb6-90b0ea71a225_1313x800.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:800,&quot;width&quot;:1313,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!atiP!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f19b758-1361-4b0d-adb6-90b0ea71a225_1313x800.png 424w, https://substackcdn.com/image/fetch/$s_!atiP!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f19b758-1361-4b0d-adb6-90b0ea71a225_1313x800.png 848w, https://substackcdn.com/image/fetch/$s_!atiP!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f19b758-1361-4b0d-adb6-90b0ea71a225_1313x800.png 1272w, https://substackcdn.com/image/fetch/$s_!atiP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6f19b758-1361-4b0d-adb6-90b0ea71a225_1313x800.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>There are two types of cookies. One is Session Cookies, and the other is Persistent Cookies. If a cookie does not contain an expiration date, it is regarded as a session cookie. Session cookies are stored in memory and are never written to disk. When the browser is closed, the cookie will be permanently lost thereafter. If a cookie contains an &#8220;expiration period&#8221;, it is regarded as a persistent cookie. At the specified expiration date, the cookie will be deleted from the disk.</p><p>There is also the &#8220;Secure and HttpOnly flags of cookies&#8221;. Let&#8217;s introduce them one by one.</p><h3><strong>Session Cookies</strong></h3><p>The example above creates a session cookie. Session cookies have a characteristic that when the client is closed, the cookie will be deleted because it does not specify the <code>Expires</code> or <code>Max-Age</code> directive.</p><p>However, web browsers may use session restoration, which will keep most session cookies in a permanent state as if the browser had never been closed.</p><h3><strong>Permanent Cookies</strong></h3><p>Permanent cookies do not expire when the client is closed. Instead, they expire after a &#8220;specific date (Expires)&#8221; or a &#8220;specific length of time (Max-Age)&#8221;. For example.</p><pre><code><span>Set-Cookie: id=a3fWa; Expires=Sat, 21 Sep 2024 11:28:00 GMT;</span></code></pre><h3><strong>Secure and HttpOnly flags of cookies</strong></h3><p>Secure cookies need to be sent to the server in an encrypted manner through the HTTPS protocol. Even if they are secure, sensitive information should not be stored in cookies because they are inherently insecure and this flag does not provide real protection.</p><p>Function of HttpOnly</p><ul><li><p>The lack of the HttpOnly property in session cookies can lead to attackers being able to obtain users&#8217; cookie information through programs (JS scripts, Applets, etc.), resulting in the leakage of users&#8217; cookie information and increasing the threat of cross-site scripting attacks by attackers.</p></li><li><p>HttpOnly is an extension made by Microsoft to cookies. This value specifies whether cookies can be accessed through client scripts.</p></li><li><p>If the HttpOnly property is not set to true in cookies, it may lead to cookie theft. Stolen cookies can contain sensitive information identifying site users, such as <a href="https://asp.net/">ASP.NET</a> session IDs or Forms authentication tickets. Attackers can replay stolen cookies to disguise as users or obtain sensitive information and conduct cross-site scripting attacks.</p></li></ul><h3><strong>Scope of cookies</strong></h3><p>The <code>Domain</code> and <code>Path</code> identifiers define the scope of cookies: that is, which URLs cookies should be sent to.</p><p>The <code>Domain</code> identifier specifies which hosts can accept cookies. If not specified, the current host (excluding subdomains) is the default. If <code>Domain</code> is specified, subdomains are generally included.</p><p>For example, if <code>Domain=mozilla.org</code> is set, cookies are also included in subdomains (such as <code>developer.mozilla.org</code>).</p><p>For example, if <code>Path=/test</code> is set, the following addresses will all match:</p><ul><li><p><code>/test</code></p></li><li><p><code>/test/user/</code></p></li><li><p><code>/test/user/login</code></p></li></ul><h2><strong>Why do we need Token when we already have Session?</strong></h2><p>In modern web development, although Session can realize user authentication and state management to a certain extent, it also has some limitations.</p><h3><strong>I. Limitations of Session</strong></h3><p>Suppose you are running a large online shopping mall. When users log in to your mall, the server will create a Session to record the user&#8217;s login status. This Session is like an exclusive card prepared for users at the mall service desk, which records the user&#8217;s identity information.</p><p>However, when your mall&#8217;s business becomes more and more busy and many users are shopping online at the same time, the server needs to save this Session information for each user, which will occupy a large amount of server memory resources. Moreover, if your mall uses multiple servers to share traffic (such as through a load balancer), then a complex mechanism is needed to ensure that when users switch between different servers, their Session information can be correctly transmitted and identified. Otherwise, users may be suddenly logged out or unable to shop normally.</p><p>In addition, suppose a user is shopping in your mall using a mobile phone and suddenly has an urgent matter and needs to go out. At this time, if the user uses another device (such as a tablet) to access your mall again outside, since Session is usually bound to a specific device, the user may need to log in again, which will bring inconvenience to the user.</p><h3><strong>II. Advantages of Token</strong></h3><p>Now, let&#8217;s introduce Token. Token is like a magic pass. After a user logs in successfully, the server will generate a Token containing the user&#8217;s identity information and return this Token to the user. The user can save this Token on their own device (such as in the local storage of the browser).</p><p>When users browse products, add to the shopping cart or check out in the mall, they only need to carry this Token in each request. After the server receives the request, it can determine the user&#8217;s identity and permissions by verifying the validity of the Token, without having to look up and manage complex Session information.</p><p>For example, a user logs in to your mall on a mobile phone and obtains a Token. When the user goes out and uses a tablet to access the mall again, only this Token needs to be provided in the browser of the tablet, and the server can immediately identify the user&#8217;s identity without the user having to log in again. Moreover, no matter how many users are online at the same time in your mall, the server does not need to save a large amount of Session information for each user. It only needs to verify the Token for each request, greatly reducing the burden on the server.</p><p>In addition, Token can be easily integrated with third-party services. For example, if your mall wants to cooperate with an external payment service, only the Token needs to be passed to the payment service. The payment service can determine the user&#8217;s identity by verifying the Token without having to establish its own Session management mechanism.</p><p>Now let&#8217;s introduce Token in detail.</p><h2><strong>What is a Token?</strong></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!L5iv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b33081f-3d83-4bc0-a1c6-79392891500f_1313x739.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!L5iv!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b33081f-3d83-4bc0-a1c6-79392891500f_1313x739.jpeg 424w, https://substackcdn.com/image/fetch/$s_!L5iv!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b33081f-3d83-4bc0-a1c6-79392891500f_1313x739.jpeg 848w, https://substackcdn.com/image/fetch/$s_!L5iv!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b33081f-3d83-4bc0-a1c6-79392891500f_1313x739.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!L5iv!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b33081f-3d83-4bc0-a1c6-79392891500f_1313x739.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!L5iv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b33081f-3d83-4bc0-a1c6-79392891500f_1313x739.jpeg" width="1313" height="739" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6b33081f-3d83-4bc0-a1c6-79392891500f_1313x739.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:739,&quot;width&quot;:1313,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!L5iv!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b33081f-3d83-4bc0-a1c6-79392891500f_1313x739.jpeg 424w, https://substackcdn.com/image/fetch/$s_!L5iv!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b33081f-3d83-4bc0-a1c6-79392891500f_1313x739.jpeg 848w, https://substackcdn.com/image/fetch/$s_!L5iv!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b33081f-3d83-4bc0-a1c6-79392891500f_1313x739.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!L5iv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b33081f-3d83-4bc0-a1c6-79392891500f_1313x739.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Photo by <a href="https://unsplash.com/@onefifith?utm_source=medium&amp;utm_medium=referral">Shubham Dhage</a> on <a href="https://unsplash.com/?utm_source=medium&amp;utm_medium=referral">Unsplash</a></p><h3><strong>1. Access Token</strong></h3><p>Access Token is a resource credential required when accessing resource interfaces (APIs).</p><p>The composition of a token is not fixed. A simple token composition includes:</p><ul><li><p><code>uid </code>(user&#8217;s unique identity identifier);</p></li><li><p><code>time </code>(timestamp of the current time);</p></li><li><p><code>sign </code>(signature, a hexadecimal string of a certain length compressed by a hash algorithm from the first few digits of the token).</p></li></ul><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;ef5ab9c8-66d6-4e21-9d70-33e3d2ad479b&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">import java.security.MessageDigest;
import java.util.Base64;
import java.util.Date;

public class TokenGenerator {

    public static String generateToken(int uid) {
        long time = new Date().getTime();
        String tokenContent = uid + &#8220;-&#8221; + time;
        // Generate a sign for the token content.
        String sign = generateSign(tokenContent);
        return uid + &#8220;-&#8221; + time + &#8220;-&#8221; + sign;
    }

    // Generate a sign for the given content using SHA-256 hash algorithm.
    private static String generateSign(String content) {
        try {
            MessageDigest digest = MessageDigest.getInstance(&#8221;SHA-256&#8221;);
            byte[] hash = digest.digest(content.getBytes());
            StringBuilder hexString = new StringBuilder();
            for (byte b : hash) {
                // Convert each byte to a two-digit hexadecimal string.
                String hex = Integer.toHexString(0xff &amp; b);
                if (hex.length() == 1) hexString.append(&#8217;0&#8217;);
                hexString.append(hex);
            }
            return hexString.substring(0, 8);
        } catch (Exception e) {
            return null;
        }
    }

    public static void main(String[] args) {
        int uid = 1234;
        String token = generateToken(uid);
        System.out.println(&#8221;Generated token: &#8220; + token);
    }
}</code></pre></div><p>Output:</p><pre><code><span>Generated token: 1234-1729530432169-3638dd14</span></code></pre><p>It has the following characteristics:</p><ul><li><p>The server is stateless and has good scalability.</p></li><li><p>Supports mobile devices.</p></li><li><p>Sufficiently secure.</p></li><li><p>Supports cross-program invocations.</p></li></ul><p>The token authentication process:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!GSoM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4ec740b-d293-422e-90b9-3c62177ea1f7_1313x506.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!GSoM!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4ec740b-d293-422e-90b9-3c62177ea1f7_1313x506.png 424w, https://substackcdn.com/image/fetch/$s_!GSoM!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4ec740b-d293-422e-90b9-3c62177ea1f7_1313x506.png 848w, https://substackcdn.com/image/fetch/$s_!GSoM!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4ec740b-d293-422e-90b9-3c62177ea1f7_1313x506.png 1272w, https://substackcdn.com/image/fetch/$s_!GSoM!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4ec740b-d293-422e-90b9-3c62177ea1f7_1313x506.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!GSoM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4ec740b-d293-422e-90b9-3c62177ea1f7_1313x506.png" width="1313" height="506" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f4ec740b-d293-422e-90b9-3c62177ea1f7_1313x506.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:506,&quot;width&quot;:1313,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!GSoM!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4ec740b-d293-422e-90b9-3c62177ea1f7_1313x506.png 424w, https://substackcdn.com/image/fetch/$s_!GSoM!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4ec740b-d293-422e-90b9-3c62177ea1f7_1313x506.png 848w, https://substackcdn.com/image/fetch/$s_!GSoM!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4ec740b-d293-422e-90b9-3c62177ea1f7_1313x506.png 1272w, https://substackcdn.com/image/fetch/$s_!GSoM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4ec740b-d293-422e-90b9-3c62177ea1f7_1313x506.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>From the above process, we can know that the token needs to be carried in every subsequent request, so the token needs to be placed in the HTTP Header. User authentication based on token is a server-side stateless authentication method, and the server does not need to store token data. The calculation time for parsing the token is exchanged for the storage space of the session, thereby reducing the pressure on the server and reducing frequent database query operations.</p><p>In addition, the token is completely managed by the application itself, so it can avoid the same-origin policy.</p><h3><strong>2.Refresh Token</strong></h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!rXjQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e1af98a-1fac-4d2d-b0f8-f22dcf157369_1313x875.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!rXjQ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e1af98a-1fac-4d2d-b0f8-f22dcf157369_1313x875.jpeg 424w, https://substackcdn.com/image/fetch/$s_!rXjQ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e1af98a-1fac-4d2d-b0f8-f22dcf157369_1313x875.jpeg 848w, https://substackcdn.com/image/fetch/$s_!rXjQ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e1af98a-1fac-4d2d-b0f8-f22dcf157369_1313x875.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!rXjQ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e1af98a-1fac-4d2d-b0f8-f22dcf157369_1313x875.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!rXjQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e1af98a-1fac-4d2d-b0f8-f22dcf157369_1313x875.jpeg" width="1313" height="875" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7e1af98a-1fac-4d2d-b0f8-f22dcf157369_1313x875.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:875,&quot;width&quot;:1313,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!rXjQ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e1af98a-1fac-4d2d-b0f8-f22dcf157369_1313x875.jpeg 424w, https://substackcdn.com/image/fetch/$s_!rXjQ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e1af98a-1fac-4d2d-b0f8-f22dcf157369_1313x875.jpeg 848w, https://substackcdn.com/image/fetch/$s_!rXjQ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e1af98a-1fac-4d2d-b0f8-f22dcf157369_1313x875.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!rXjQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7e1af98a-1fac-4d2d-b0f8-f22dcf157369_1313x875.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Photo by <a href="https://unsplash.com/@genessapana?utm_source=medium&amp;utm_medium=referral">Genessa Panainte</a> on <a href="https://unsplash.com/?utm_source=medium&amp;utm_medium=referral">Unsplash</a></p><p>Refresh token is another kind of token that is dedicated to refreshing the access token. If there is no refresh token, the access token can also be refreshed, but every time it is refreshed, the user needs to enter the login username and password, which will be very troublesome. With the refresh token, this trouble can be reduced. The client directly uses the refresh token to update the access token without the user performing additional operations.</p><p>The validity period of the Access Token is usually relatively short. When the Access Token becomes invalid due to expiration, a new token can be obtained by using the Refresh Token. If the Refresh Token also expires, the user can only log in again.</p><p>In addition, the Refresh Token and expiration time are stored in the server&#8217;s database and are only verified when applying for a new Access Token. It will not affect the response time of business interfaces and does not need to be kept in memory all the time like Session to handle a large number of requests.</p><h2><strong>JSON Web Token</strong></h2><p>Token is a broader concept, while JSON Web Token (JWT) is a token with specific structure and characteristics. JWT has advantages in some scenarios, such as when self-contained authentication information is needed, cross-platform use, and when there are high requirements for scalability. We will provide a specific introduction to JWT in later articles.</p><div><hr></div><p>Okay, that&#8217;s all for this time, see you next time! &#129325;</p><p><strong>Finally, if the article was helpful, please clap &#128079;and follow, thank you! &#9584;(*&#176;&#9661;&#176;*)&#9583;</strong></p><p><strong>I&#8217;m Dylan, looking forward to progressing with you. &#10084;&#65039;</strong></p>]]></content:encoded></item><item><title><![CDATA[These Five Most Commonly Used Encryption Algorithms You Must Master!😉]]></title><description><![CDATA[Understanding irreversible, symmetric, and asymmetric encryption mechanics, Java code implementations, and HTTPS fundamentals]]></description><link>https://seabreeze0828.substack.com/p/these-five-most-commonly-used-encryption</link><guid isPermaLink="false">https://seabreeze0828.substack.com/p/these-five-most-commonly-used-encryption</guid><dc:creator><![CDATA[Dylan Smith]]></dc:creator><pubDate>Sat, 15 Aug 2026 16:03:22 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Sfpq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1544260-1987-4c6d-8225-1c579ce1f650_1050x584.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Sfpq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1544260-1987-4c6d-8225-1c579ce1f650_1050x584.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Sfpq!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1544260-1987-4c6d-8225-1c579ce1f650_1050x584.png 424w, https://substackcdn.com/image/fetch/$s_!Sfpq!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1544260-1987-4c6d-8225-1c579ce1f650_1050x584.png 848w, https://substackcdn.com/image/fetch/$s_!Sfpq!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1544260-1987-4c6d-8225-1c579ce1f650_1050x584.png 1272w, https://substackcdn.com/image/fetch/$s_!Sfpq!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1544260-1987-4c6d-8225-1c579ce1f650_1050x584.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Sfpq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1544260-1987-4c6d-8225-1c579ce1f650_1050x584.png" width="1050" height="584" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d1544260-1987-4c6d-8225-1c579ce1f650_1050x584.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:584,&quot;width&quot;:1050,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Sfpq!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1544260-1987-4c6d-8225-1c579ce1f650_1050x584.png 424w, https://substackcdn.com/image/fetch/$s_!Sfpq!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1544260-1987-4c6d-8225-1c579ce1f650_1050x584.png 848w, https://substackcdn.com/image/fetch/$s_!Sfpq!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1544260-1987-4c6d-8225-1c579ce1f650_1050x584.png 1272w, https://substackcdn.com/image/fetch/$s_!Sfpq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd1544260-1987-4c6d-8225-1c579ce1f650_1050x584.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://seabreeze0828.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">The Java Knowledge Ocean is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>Digital signature and information encryption are technologies that are often used in both front-end and back-end development. Application scenarios include:</p><ul><li><p>Users&#8217; passwords cannot be directly stored in plaintext. Encrypted ciphertext should be stored.</p></li><li><p>Sensitive data such as users&#8217; bank card numbers and ID numbers need to be encrypted for transmission.</p></li><li><p>For some important interfaces, such as payment, the client needs to generate a signature for the request, and the server needs to verify the signature to check whether this request is safe.<br>-&#8230;</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_!04LU!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa246b45d-4e3f-4cc5-8073-a8d518c121b1_498x374.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!04LU!,w_424,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa246b45d-4e3f-4cc5-8073-a8d518c121b1_498x374.gif 424w, https://substackcdn.com/image/fetch/$s_!04LU!,w_848,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa246b45d-4e3f-4cc5-8073-a8d518c121b1_498x374.gif 848w, https://substackcdn.com/image/fetch/$s_!04LU!,w_1272,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa246b45d-4e3f-4cc5-8073-a8d518c121b1_498x374.gif 1272w, https://substackcdn.com/image/fetch/$s_!04LU!,w_1456,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa246b45d-4e3f-4cc5-8073-a8d518c121b1_498x374.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!04LU!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa246b45d-4e3f-4cc5-8073-a8d518c121b1_498x374.gif" width="498" height="374" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a246b45d-4e3f-4cc5-8073-a8d518c121b1_498x374.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:374,&quot;width&quot;:498,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!04LU!,w_424,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa246b45d-4e3f-4cc5-8073-a8d518c121b1_498x374.gif 424w, https://substackcdn.com/image/fetch/$s_!04LU!,w_848,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa246b45d-4e3f-4cc5-8073-a8d518c121b1_498x374.gif 848w, https://substackcdn.com/image/fetch/$s_!04LU!,w_1272,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa246b45d-4e3f-4cc5-8073-a8d518c121b1_498x374.gif 1272w, https://substackcdn.com/image/fetch/$s_!04LU!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa246b45d-4e3f-4cc5-8073-a8d518c121b1_498x374.gif 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Different application scenarios require different signature encryption algorithms to achieve business goals. Here, I will briefly introduce several common signature encryption algorithms and their applications in some typical scenarios.</p><h2><strong>Common encryption algorithms</strong></h2><p>In general, encryption algorithms can be divided into irreversible encryption and reversible encryption. Reversible encryption can be further divided into symmetric encryption and asymmetric encryption.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Z4-U!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24a89f3b-1848-4086-af16-aeeb5c157f14_1050x490.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Z4-U!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24a89f3b-1848-4086-af16-aeeb5c157f14_1050x490.png 424w, https://substackcdn.com/image/fetch/$s_!Z4-U!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24a89f3b-1848-4086-af16-aeeb5c157f14_1050x490.png 848w, https://substackcdn.com/image/fetch/$s_!Z4-U!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24a89f3b-1848-4086-af16-aeeb5c157f14_1050x490.png 1272w, https://substackcdn.com/image/fetch/$s_!Z4-U!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24a89f3b-1848-4086-af16-aeeb5c157f14_1050x490.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Z4-U!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24a89f3b-1848-4086-af16-aeeb5c157f14_1050x490.png" width="1050" height="490" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/24a89f3b-1848-4086-af16-aeeb5c157f14_1050x490.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:490,&quot;width&quot;:1050,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!Z4-U!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24a89f3b-1848-4086-af16-aeeb5c157f14_1050x490.png 424w, https://substackcdn.com/image/fetch/$s_!Z4-U!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24a89f3b-1848-4086-af16-aeeb5c157f14_1050x490.png 848w, https://substackcdn.com/image/fetch/$s_!Z4-U!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24a89f3b-1848-4086-af16-aeeb5c157f14_1050x490.png 1272w, https://substackcdn.com/image/fetch/$s_!Z4-U!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F24a89f3b-1848-4086-af16-aeeb5c157f14_1050x490.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><strong>Irreversible algorithms</strong></h2><p>The encryption of an irreversible encryption algorithm is irreversible, that is, the ciphertext cannot be restored to the original text.</p><p><mark data-color="rgb(232, 243, 232)" style="background-color: rgb(232, 243, 232); color: rgb(0, 0, 0);">A hash algorithm is an irreversible algorithm</mark>. In a hash algorithm, plaintext generates a hash value through a hash algorithm. The hash value is data with a fixed length and is independent of the length of the plaintext.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!h9_n!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac168ac7-b04f-4c27-8d2f-39791248dc62_970x196.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!h9_n!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac168ac7-b04f-4c27-8d2f-39791248dc62_970x196.png 424w, https://substackcdn.com/image/fetch/$s_!h9_n!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac168ac7-b04f-4c27-8d2f-39791248dc62_970x196.png 848w, https://substackcdn.com/image/fetch/$s_!h9_n!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac168ac7-b04f-4c27-8d2f-39791248dc62_970x196.png 1272w, https://substackcdn.com/image/fetch/$s_!h9_n!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac168ac7-b04f-4c27-8d2f-39791248dc62_970x196.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!h9_n!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac168ac7-b04f-4c27-8d2f-39791248dc62_970x196.png" width="970" height="196" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ac168ac7-b04f-4c27-8d2f-39791248dc62_970x196.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:196,&quot;width&quot;:970,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!h9_n!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac168ac7-b04f-4c27-8d2f-39791248dc62_970x196.png 424w, https://substackcdn.com/image/fetch/$s_!h9_n!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac168ac7-b04f-4c27-8d2f-39791248dc62_970x196.png 848w, https://substackcdn.com/image/fetch/$s_!h9_n!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac168ac7-b04f-4c27-8d2f-39791248dc62_970x196.png 1272w, https://substackcdn.com/image/fetch/$s_!h9_n!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac168ac7-b04f-4c27-8d2f-39791248dc62_970x196.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>There are many specific implementations of hash algorithms. Common ones include MD5, SHA1, SHA-224, SHA-256, and so on.</p><p>Hash algorithms are often used in scenarios such as digital signatures, message authentication, and password storage.</p><p>Hash algorithms do not require a key. Of course, there are also some irreversible algorithms that require a key, such as the HMAC algorithm.</p><h3><strong>MD5</strong></h3><p>MD5, fully known as &#8220;Message-Digest Algorithm 5&#8221;. It can generate a fixed-length hash value by applying a hash algorithm to data of any length. The output length of the MD5 algorithm is 128 bits, usually represented by 32 hexadecimal numbers.</p><p>Let&#8217;s take a look at the Java code implementation of the MD5 algorithm:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;3e93cf04-7460-4d18-92ec-56bed527cfd6&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">import java.security.MessageDigest;
import java.util.Formatter;

public class MD5 {
    private static final String MD5_ALGORITHM = &#8220;MD5&#8221;;
    public static String encrypt(String data) throws Exception {
        // Obtain an instance of the MD5 algorithm.
        MessageDigest messageDigest = MessageDigest.getInstance(MD5_ALGORITHM);
        // Calculate the hash value.
        byte[] digest = messageDigest.digest(data.getBytes());
        Formatter formatter = new Formatter();
        // Pad leading zeros and format.
        for (byte b : digest) {
            formatter.format(&#8221;%02x&#8221;, b);
        }
        return formatter.toString();
    }

    public static void main(String[] args) throws Exception {
        String data = &#8220;Hello Dylan&#8221;;
        String encryptedData = encrypt(data);
        System.out.println(&#8221;Encrypted data: &#8220; + encryptedData);
    }
}</code></pre></div><p>Output:</p><pre><code><span>Encrypted data: ac1fbf0692d337780a0fbffb2798ac96</span></code></pre><p>MD5 has some advantages, such as fast calculation speed, fixed output length, and wide application.</p><p>However, as an encryption algorithm, it has a fatal drawback, that is, it is &#8220;unsafe&#8221;.</p><p>The MD5 algorithm has been broken. Moreover, the output length of the MD5 algorithm is limited. Attackers can find hash values identical to the original data through methods such as brute-force cracking or rainbow table attacks, thereby cracking the data.</p><p>Although the security can be increased by adding salt, that is, by adding some unfixed strings to the original text, it is completely possible to replace it with a more secure SHA series algorithm.</p><h3><strong>SHA-256</strong></h3><p>SHA (Secure Hash Algorithm) is a set of cryptographic hash functions used to map data of any length to a fixed-length hash value. The SHA series of algorithms was designed by the National Security Agency (NSA) in 1993. Currently, there are three versions: SHA-1, SHA-2, and SHA-3.</p><p>Among them, the SHA-1 series has defects and is no longer recommended for use.</p><p>The SHA-2 algorithm includes four hash functions: <code>SHA-224</code>, <code>SHA-256</code>, <code>SHA-384</code>, and <code>SHA-512</code>, which map data of any length to hash values of 224 bits, 256 bits, 384 bits, and 512 bits respectively.</p><p>Let&#8217;s take a look at the Java code implementation of the most commonly used <code>SHA-256</code>:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;e8306e07-ec80-47e7-9744-3f4f47dcd516&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">import java.security.MessageDigest;

public class SHA256 {
    private static final String SHA_256_ALGORITHM = &#8220;SHA-256&#8221;;
    public static String encrypt(String data) throws Exception {
        // Obtain an instance of the SHA-256 algorithm.
        MessageDigest messageDigest = MessageDigest.getInstance(SHA_256_ALGORITHM);
        // Compute the hash value.
        byte[] digest = messageDigest.digest(data.getBytes());
        StringBuilder stringBuilder = new StringBuilder();
        // Convert byte array to a hexadecimal string.
        for (byte b : digest) {
            stringBuilder.append(Integer.toHexString((b &amp; 0xFF) | 0x100), 1, 3);
        }
        return stringBuilder.toString();
    }

    public static void main(String[] args) throws Exception {
        String data = &#8220;Hello Dylan&#8221;;
        String encryptedData = encrypt(data);
        System.out.println(&#8221;Encrypted data: &#8220; + encryptedData);
    }
}</code></pre></div><p>Output:</p><pre><code><span>Encrypted data: 2ff61b5259b01a8ef4e6dae816d3f26b4b9cbc3957a6a9d0aceb49d11d5d950e</span></code></pre><p>The reason why the SHA-2 algorithm is stronger than MD5 mainly lies in two aspects:</p><ul><li><p>Longer hash value length: For example, the hash value length of the SHA-256 algorithm is 256 bits, while that of the MD5 algorithm is 128 bits. This increases the difficulty for attackers to perform brute-force cracking or rainbow table attacks.</p></li><li><p>Stronger collision resistance: The SHA algorithm uses a more complex operation process and more rounds, making it more difficult for attackers to find collisions through precomputation or coincidence.</p></li></ul><p>Of course, SHA-2 is not absolutely safe either. Hash algorithms all have the risk of being brute-force cracked or attacked by rainbow tables. Therefore, in practical applications, adding salt is still essential.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!OLYy!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde3829ad-27fb-43ee-88de-06de02892225_220x220.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!OLYy!,w_424,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde3829ad-27fb-43ee-88de-06de02892225_220x220.gif 424w, https://substackcdn.com/image/fetch/$s_!OLYy!,w_848,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde3829ad-27fb-43ee-88de-06de02892225_220x220.gif 848w, https://substackcdn.com/image/fetch/$s_!OLYy!,w_1272,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde3829ad-27fb-43ee-88de-06de02892225_220x220.gif 1272w, https://substackcdn.com/image/fetch/$s_!OLYy!,w_1456,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde3829ad-27fb-43ee-88de-06de02892225_220x220.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!OLYy!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde3829ad-27fb-43ee-88de-06de02892225_220x220.gif" width="320" height="320" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/de3829ad-27fb-43ee-88de-06de02892225_220x220.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:220,&quot;width&quot;:220,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!OLYy!,w_424,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde3829ad-27fb-43ee-88de-06de02892225_220x220.gif 424w, https://substackcdn.com/image/fetch/$s_!OLYy!,w_848,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde3829ad-27fb-43ee-88de-06de02892225_220x220.gif 848w, https://substackcdn.com/image/fetch/$s_!OLYy!,w_1272,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde3829ad-27fb-43ee-88de-06de02892225_220x220.gif 1272w, https://substackcdn.com/image/fetch/$s_!OLYy!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fde3829ad-27fb-43ee-88de-06de02892225_220x220.gif 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h2><strong>Symmetric encryption algorithm</strong></h2><p>In symmetric encryption algorithms, the same key is used for encryption and decryption.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!hFlF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2f19380-9d6b-44d2-bcb5-bceddcea72ff_1050x253.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!hFlF!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2f19380-9d6b-44d2-bcb5-bceddcea72ff_1050x253.png 424w, https://substackcdn.com/image/fetch/$s_!hFlF!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2f19380-9d6b-44d2-bcb5-bceddcea72ff_1050x253.png 848w, https://substackcdn.com/image/fetch/$s_!hFlF!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2f19380-9d6b-44d2-bcb5-bceddcea72ff_1050x253.png 1272w, https://substackcdn.com/image/fetch/$s_!hFlF!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2f19380-9d6b-44d2-bcb5-bceddcea72ff_1050x253.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!hFlF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2f19380-9d6b-44d2-bcb5-bceddcea72ff_1050x253.png" width="1050" height="253" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e2f19380-9d6b-44d2-bcb5-bceddcea72ff_1050x253.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:253,&quot;width&quot;:1050,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!hFlF!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2f19380-9d6b-44d2-bcb5-bceddcea72ff_1050x253.png 424w, https://substackcdn.com/image/fetch/$s_!hFlF!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2f19380-9d6b-44d2-bcb5-bceddcea72ff_1050x253.png 848w, https://substackcdn.com/image/fetch/$s_!hFlF!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2f19380-9d6b-44d2-bcb5-bceddcea72ff_1050x253.png 1272w, https://substackcdn.com/image/fetch/$s_!hFlF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2f19380-9d6b-44d2-bcb5-bceddcea72ff_1050x253.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>The encryption and decryption processes use the same key, so the security of the key is of utmost importance. If the key is leaked, attackers can easily crack the encrypted data.</p><p>Common symmetric encryption algorithms include DES, 3DES, AES, etc. Among them, the AES algorithm is currently one of the most widely used symmetric encryption algorithms and has relatively high security and encryption efficiency.</p><h3><strong>DES</strong></h3><p>DES (Data Encryption Standard) is a symmetric encryption algorithm developed by IBM in 1975 and is one of the earliest symmetric encryption algorithms widely used.</p><p>The DES algorithm uses a 56-bit key to encrypt data. In the encryption process, operations such as permutation, substitution, and exclusive OR are used, which has high security.</p><p>Let&#8217;s take a look at the Java code implementation of the DES algorithm:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;2215cde6-55da-4f75-9d96-6a949f00526b&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key;
import java.security.SecureRandom;
import java.util.Base64;

public class DES {
    private static final String DES_ALGORITHM = &#8220;DES&#8221;;

    /**
     * Encrypt data using DES algorithm.
     *
     * @param data The data to be encrypted.
     * @param key  The key for encryption, must be 8 characters long.
     * @return The encrypted data encoded in Base64.
     */
    public static String encrypt(String data, String key) throws Exception {
        // Generate key specification based on the key.
        KeySpec keySpec = new DESKeySpec(key.getBytes());
        // Generate secret key factory based on the algorithm.
        SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(DES_ALGORITHM);
        // Generate secret key based on the key factory and key specification.
        SecretKey secretKey = secretKeyFactory.generateSecret(keySpec);

        // Get cipher instance for the algorithm.
        Cipher cipher = Cipher.getInstance(DES_ALGORITHM);
        // Initialize the cipher for encryption mode and the secret key.
        cipher.init(Cipher.ENCRYPT_MODE, secretKey);
        // Encrypt the data.
        byte[] encryptedData = cipher.doFinal(data.getBytes());
        // Encode the encrypted data using Base64.
        return Base64.getEncoder().encodeToString(encryptedData);
    }

    /**
     * Decrypt data encrypted with DES algorithm.
     *
     * @param encryptedData The encrypted data encoded in Base64.
     * @param key           The key for decryption, must be 8 characters long.
     * @return The decrypted data.
     */
    public static String decrypt(String encryptedData, String key) throws Exception {
        // Generate key specification based on the key.
        KeySpec keySpec = new DESKeySpec(key.getBytes());
        // Generate secret key factory based on the algorithm.
        SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance(DES_ALGORITHM);
        // Generate secret key based on the key factory and key specification.
        SecretKey secretKey = secretKeyFactory.generateSecret(keySpec);

        // Decode the encrypted data using Base64.
        byte[] decodedData = Base64.getDecoder().decode(encryptedData);
        // Get cipher instance for the algorithm.
        Cipher cipher = Cipher.getInstance(DES_ALGORITHM);
        // Initialize the cipher for decryption mode and the secret key.
        cipher.init(Cipher.DECRYPT_MODE, secretKey);
        // Decrypt the data.
        byte[] decryptedData = cipher.doFinal(decodedData);
        // Convert the decrypted data to a string.
        return new String(decryptedData);
    }

    public static void main(String[] args) throws Exception {
        String data = &#8220;Hello Dylan&#8221;;
        String key = &#8220;12345678&#8221;; 

        String encryptedData = encrypt(data, key);
        System.out.println(&#8221;Encrypted data: &#8220; + encryptedData);

        String decryptedData = decrypt(encryptedData, key);
        System.out.println(&#8221;Decrypted data: &#8220; + decryptedData);
    }
}</code></pre></div><p>Output:</p><pre><code><span>Encrypted data: 3D12G4wkjn4CESZ5YgCeKA==
Decrypted data: Hello Dylan</span></code></pre><p>The DES algorithm is relatively fast, but it is not the optimal choice in terms of security. Because the key length of the DES algorithm is relatively short, there is a relatively high risk of being brute-force cracked and differentially attacked.</p><p>Generally, some more secure symmetric encryption algorithms such as 3DES and AES are recommended.</p><h3><strong>AES</strong></h3><p>AES (Advanced Encryption Standard), that is, the advanced encryption standard, is a symmetric encryption algorithm and is widely used in the field of data encryption and protection. The key length used by the AES algorithm is 128 bits, 192 bits, or 256 bits, which is longer than the key length of the DES algorithm and has higher security.</p><p>Let&#8217;s take a look at the Java code implementation of the AES algorithm:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;25a5fb0f-aae9-4f68-8884-1dc28543f43c&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;

public class AES {
    private static final String AES_ALGORITHM = &#8220;AES&#8221;;
    // AES encryption mode is CBC and padding method is PKCS5Padding.
    private static final String AES_TRANSFORMATION = &#8220;AES/CBC/PKCS5Padding&#8221;;
    // AES key is 16 characters.
    private static final String AES_KEY = &#8220;1234567890123456&#8221;;
    // AES initialization vector is 16 characters.
    private static final String AES_IV = &#8220;abcdefghijklmnop&#8221;;

    /**
     * Encrypt data using AES algorithm.
     *
     * @param data The data to be encrypted.
     * @return The encrypted data encoded in Base64.
     */
    public static String encrypt(String data) throws Exception {
        // Convert AES key to SecretKeySpec object.
        SecretKeySpec secretKeySpec = new SecretKeySpec(AES_KEY.getBytes(), AES_ALGORITHM);
        // Convert AES initialization vector to IvParameterSpec object.
        IvParameterSpec ivParameterSpec = new IvParameterSpec(AES_IV.getBytes());
        // Get cipher instance for the AES algorithm.
        Cipher cipher = Cipher.getInstance(AES_TRANSFORMATION);
        // Initialize the cipher for encryption mode, key and initialization vector.
        cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
        // Encrypt the data.
        byte[] encryptedData = cipher.doFinal(data.getBytes(java.nio.charset.StandardCharsets.UTF_8));
        // Encode the encrypted data using Base64.
        return Base64.getEncoder().encodeToString(encryptedData);
    }

    /**
     * Decrypt data encrypted with AES algorithm.
     *
     * @param encryptedData The encrypted data encoded in Base64.
     * @return The decrypted data.
     */
    public static String decrypt(String encryptedData) throws Exception {
        // Convert AES key to SecretKeySpec object.
        SecretKeySpec secretKeySpec = new SecretKeySpec(AES_KEY.getBytes(), AES_ALGORITHM);
        // Convert AES initialization vector to IvParameterSpec object.
        IvParameterSpec ivParameterSpec = new IvParameterSpec(AES_IV.getBytes());
        // Get cipher instance for the AES algorithm.
        Cipher cipher = Cipher.getInstance(AES_TRANSFORMATION);
        // Initialize the cipher for decryption mode, key and initialization vector.
        cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
        // Decode the encrypted data using Base64.
        byte[] decodedData = Base64.getDecoder().decode(encryptedData);
        // Decrypt the data.
        byte[] decryptedData = cipher.doFinal(decodedData);
        // Return the decrypted data.
        return new String(decryptedData, java.nio.charset.StandardCharsets.UTF_8);
    }

    public static void main(String[] args) throws Exception {
        String data = &#8220;Hello World&#8221;;

        String encryptedData = encrypt(data);
        System.out.println(&#8221;Encrypted data: &#8220; + encryptedData);

        String decryptedData = decrypt(encryptedData);
        System.out.println(&#8221;Decrypted data: &#8220; + decryptedData);
    }
}</code></pre></div><p>Output:</p><pre><code><span>Encrypted data: H+v+5je7lD1T5xZZC0kdqQ==
Decrypted data: Hello Dylan</span></code></pre><p>The AES algorithm uses a longer key length, a larger key space, and higher security, and can effectively resist brute-force cracking attacks.</p><p>Of course, because the key length is longer, more storage is required.</p><p>For symmetric encryption algorithms, the biggest pain point is the difficulty in key management. In contrast, asymmetric encryption does not have this concern.</p><h2><strong>Asymmetric encryption algorithm</strong></h2><p>Asymmetric encryption algorithms require two keys. These two keys are different from each other but are mutually matched. One is called the &#8220;public key&#8221; and the other is called the &#8220;private key&#8221;.</p><p>If one of them is used for encryption, then the other is used for decryption. For example, if public key encryption is used, then private key decryption is required.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!eWMk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5af8abca-c24d-48c8-ad97-abbd53fa9903_1050x275.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!eWMk!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5af8abca-c24d-48c8-ad97-abbd53fa9903_1050x275.png 424w, https://substackcdn.com/image/fetch/$s_!eWMk!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5af8abca-c24d-48c8-ad97-abbd53fa9903_1050x275.png 848w, https://substackcdn.com/image/fetch/$s_!eWMk!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5af8abca-c24d-48c8-ad97-abbd53fa9903_1050x275.png 1272w, https://substackcdn.com/image/fetch/$s_!eWMk!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5af8abca-c24d-48c8-ad97-abbd53fa9903_1050x275.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!eWMk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5af8abca-c24d-48c8-ad97-abbd53fa9903_1050x275.png" width="1050" height="275" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5af8abca-c24d-48c8-ad97-abbd53fa9903_1050x275.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:275,&quot;width&quot;:1050,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!eWMk!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5af8abca-c24d-48c8-ad97-abbd53fa9903_1050x275.png 424w, https://substackcdn.com/image/fetch/$s_!eWMk!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5af8abca-c24d-48c8-ad97-abbd53fa9903_1050x275.png 848w, https://substackcdn.com/image/fetch/$s_!eWMk!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5af8abca-c24d-48c8-ad97-abbd53fa9903_1050x275.png 1272w, https://substackcdn.com/image/fetch/$s_!eWMk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5af8abca-c24d-48c8-ad97-abbd53fa9903_1050x275.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3><strong>RSA</strong></h3><p>The RSA algorithm is currently the most widely used asymmetric encryption algorithm. It was invented by Ron Rivest, Adi Shamir, and Leonard Adleman in 1978. The name comes from the first letters of the surnames of the three people.</p><p>Let&#8217;s take a look at the Java implementation of the RSA algorithm:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;905c84cd-f370-4686-b74f-2fcd803bd833&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">import javax.crypto.Cipher;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.PrivateKey;
import java.util.Base64;

public class RSA {
    private static final String RSA_ALGORITHM = &#8220;RSA&#8221;;

    /**
     * Generate an RSA key pair.
     *
     * @return The RSA key pair.
     */
    public static KeyPair generateKeyPair() throws NoSuchAlgorithmException {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(RSA_ALGORITHM);
        keyPairGenerator.initialize(2048); // Key size is 2048 bits.
        return keyPairGenerator.generateKeyPair();
    }

    /**
     * Encrypt data using the public key.
     *
     * @param data      The data to be encrypted.
     * @param publicKey The public key.
     * @return The encrypted data.
     */
    public static String encrypt(String data, PublicKey publicKey) throws Exception {
        Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        byte[] encryptedData = cipher.doFinal(data.getBytes(java.nio.charset.StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(encryptedData);
    }

    /**
     * Decrypt data using the private key.
     *
     * @param encryptedData The encrypted data.
     * @param privateKey    The private key.
     * @return The decrypted data.
     */
    public static String decrypt(String encryptedData, PrivateKey privateKey) throws Exception {
        byte[] decodedData = Base64.getDecoder().decode(encryptedData);
        Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        byte[] decryptedData = cipher.doFinal(decodedData);
        return new String(decryptedData, java.nio.charset.StandardCharsets.UTF_8);
    }

    public static void main(String[] args) throws Exception {
        KeyPair keyPair = generateKeyPair();
        PublicKey publicKey = keyPair.getPublic();
        PrivateKey privateKey = keyPair.getPrivate();

        String data = &#8220;Hello World&#8221;;

        String encryptedData = encrypt(data, publicKey);
        System.out.println(&#8221;Encrypted data: &#8220; + encryptedData);

        String decryptedData = decrypt(encryptedData, privateKey);
        System.out.println(&#8221;Decrypted data: &#8220; + decryptedData);
    }
}</code></pre></div><p>Output:</p><pre><code><span>Encrypted data: kLKl+j90w34MwDu+k933znMxHnnajJeJCqIOfaYbiYT2IJrTmTisIGWvCJvtyprgMidObGxzhWAWs0lQKsq+dHZ70XG8m3ICMy4CxCYVdqOymEiSjD4HfEhKAZc58TDQ0J0qPdOpw+fqkmxsSG2sQmI87ffLx6ChbbuZlVrwJmm2KwuqWEwxuddilntsnZWLVn4uEXtHzHhrXm8RqOodVdLUemUxcV7N9x9v3DJyei7zZbTu2OEbija0bIHukQ9/yqZBbGSANfa8LVsQ+rsiKWd7+IGbPmsXmds8G6hgkkP1K2aHecOYgc8bo2vrY2httxadZzwqkknqn47eU+3cNg==
Decrypted data: Hello Dylan</span></code></pre><p>The advantages of the RSA algorithm are high security. The public key can be made public, and the private key must be kept secret, ensuring data security. It can be used in multiple application scenarios such as digital signatures and key negotiation.</p><p>The disadvantages are that the encryption and decryption speed is relatively slow. The longer the key length, the longer the encryption and decryption time. If the key length is too short, it is easy to be brute-force cracked. If the key length is too long, it will increase the computational cost and storage space overhead.</p><h2><strong>Summary</strong></h2><p>In this issue, we have simply reviewed the five most commonly used encryption algorithms.</p><p>In fact, when it comes to the application of encryption and decryption algorithms, there is one thing that can be said to be applied to the extreme. What is it?</p><p>&#8212; <strong>HTTPS</strong>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!FPb1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcfa551ae-beb5-4b88-b96b-a5b51b520217_220x244.gif" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!FPb1!,w_424,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcfa551ae-beb5-4b88-b96b-a5b51b520217_220x244.gif 424w, https://substackcdn.com/image/fetch/$s_!FPb1!,w_848,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcfa551ae-beb5-4b88-b96b-a5b51b520217_220x244.gif 848w, https://substackcdn.com/image/fetch/$s_!FPb1!,w_1272,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcfa551ae-beb5-4b88-b96b-a5b51b520217_220x244.gif 1272w, https://substackcdn.com/image/fetch/$s_!FPb1!,w_1456,c_limit,f_webp,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcfa551ae-beb5-4b88-b96b-a5b51b520217_220x244.gif 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!FPb1!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcfa551ae-beb5-4b88-b96b-a5b51b520217_220x244.gif" width="320" height="354.90909090909093" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cfa551ae-beb5-4b88-b96b-a5b51b520217_220x244.gif&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:244,&quot;width&quot;:220,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!FPb1!,w_424,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcfa551ae-beb5-4b88-b96b-a5b51b520217_220x244.gif 424w, https://substackcdn.com/image/fetch/$s_!FPb1!,w_848,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcfa551ae-beb5-4b88-b96b-a5b51b520217_220x244.gif 848w, https://substackcdn.com/image/fetch/$s_!FPb1!,w_1272,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcfa551ae-beb5-4b88-b96b-a5b51b520217_220x244.gif 1272w, https://substackcdn.com/image/fetch/$s_!FPb1!,w_1456,c_limit,f_auto,q_auto:good,fl_lossy/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcfa551ae-beb5-4b88-b96b-a5b51b520217_220x244.gif 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>In conclusion, HTTPS uses symmetric encryption algorithms, asymmetric encryption algorithms, and hash algorithms to ensure the security and integrity of data, thereby ensuring the identity and data security of both communicating parties.</p><p>We will conduct a detailed exploration in a later article~&#128516;</p><div><hr></div><p>Okay, that&#8217;s all for this time, see you next time! <mark data-color="rgb(232, 243, 232)" style="background-color: rgb(232, 243, 232); color: rgb(0, 0, 0);">&#129325;</mark></p><p><strong>Finally, if the article was helpful, please clap &#128079;and follow, thank you! &#9584;(*&#176;&#9661;&#176;*)&#9583;</strong></p><p><strong>I&#8217;m Dylan, looking forward to progressing with you. &#10084;&#65039;</strong></p>]]></content:encoded></item><item><title><![CDATA[Interviewer: Why Must the Password Be Reset When Forgotten Instead of Directly Telling Me the Original Password?]]></title><description><![CDATA[Demystifying Password Security: One-Way Hashing, Salting, and the Mechanics Behind Password Resets]]></description><link>https://seabreeze0828.substack.com/p/interviewer-why-must-the-password</link><guid isPermaLink="false">https://seabreeze0828.substack.com/p/interviewer-why-must-the-password</guid><dc:creator><![CDATA[Dylan Smith]]></dc:creator><pubDate>Sat, 15 Aug 2026 15:33:44 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!Ng1o!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf754481-c582-4057-ba01-fd587a34a0de_1313x551.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://seabreeze0828.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">The Java Knowledge Ocean is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p><span>R</span>ecently, a friend encountered a very interesting question in an interview. I believe that most friends may not have encountered it.</p><p>The interviewer asked: &#8220;<strong>Why do many websites require password reset when forgetting the password instead of directly telling users the original password?&#8221;</strong> (&#8857;&#717;&#8857;)&#65311;</p><p>It&#8217;s a very interesting question, right? Many websites have the function of &#8220;<strong>Forgot password?</strong>&#8221;, but why when we click on forgot password and go through a bunch of verifications, the website will let us reset the password instead of directly telling us the original password?</p><p>So, let&#8217;s talk about this question today.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Ng1o!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf754481-c582-4057-ba01-fd587a34a0de_1313x551.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Ng1o!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf754481-c582-4057-ba01-fd587a34a0de_1313x551.png 424w, https://substackcdn.com/image/fetch/$s_!Ng1o!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf754481-c582-4057-ba01-fd587a34a0de_1313x551.png 848w, https://substackcdn.com/image/fetch/$s_!Ng1o!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf754481-c582-4057-ba01-fd587a34a0de_1313x551.png 1272w, https://substackcdn.com/image/fetch/$s_!Ng1o!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf754481-c582-4057-ba01-fd587a34a0de_1313x551.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Ng1o!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf754481-c582-4057-ba01-fd587a34a0de_1313x551.png" width="1313" height="551" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/af754481-c582-4057-ba01-fd587a34a0de_1313x551.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:551,&quot;width&quot;:1313,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!Ng1o!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf754481-c582-4057-ba01-fd587a34a0de_1313x551.png 424w, https://substackcdn.com/image/fetch/$s_!Ng1o!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf754481-c582-4057-ba01-fd587a34a0de_1313x551.png 848w, https://substackcdn.com/image/fetch/$s_!Ng1o!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf754481-c582-4057-ba01-fd587a34a0de_1313x551.png 1272w, https://substackcdn.com/image/fetch/$s_!Ng1o!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf754481-c582-4057-ba01-fd587a34a0de_1313x551.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><strong>Preventing information leakage</strong></h2><p>For most people, in order to prevent forgetting too many passwords, many times everyone will use a unified password! That is to say, your multiple accounts may all have the same password. Therefore, once the password is leaked, it may affect multiple of your accounts, even bank card accounts.</p><p>Therefore, for websites (especially some large websites), protecting user data security is an extremely important matter.</p><p>So, the data you save on the website may not be so safe.</p><p>For most people, the most relevant experience (seen online) should be scam calls. They can even tell you all your personal information very clearly. So where did this information come from?</p><p>Mostly due to data leakage, all your personal information is known to others.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!8Ogd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F840d060e-f378-47d3-9fc6-c213a60b3748_1313x875.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!8Ogd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F840d060e-f378-47d3-9fc6-c213a60b3748_1313x875.jpeg 424w, https://substackcdn.com/image/fetch/$s_!8Ogd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F840d060e-f378-47d3-9fc6-c213a60b3748_1313x875.jpeg 848w, https://substackcdn.com/image/fetch/$s_!8Ogd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F840d060e-f378-47d3-9fc6-c213a60b3748_1313x875.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!8Ogd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F840d060e-f378-47d3-9fc6-c213a60b3748_1313x875.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!8Ogd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F840d060e-f378-47d3-9fc6-c213a60b3748_1313x875.jpeg" width="1313" height="875" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/840d060e-f378-47d3-9fc6-c213a60b3748_1313x875.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:875,&quot;width&quot;:1313,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!8Ogd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F840d060e-f378-47d3-9fc6-c213a60b3748_1313x875.jpeg 424w, https://substackcdn.com/image/fetch/$s_!8Ogd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F840d060e-f378-47d3-9fc6-c213a60b3748_1313x875.jpeg 848w, https://substackcdn.com/image/fetch/$s_!8Ogd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F840d060e-f378-47d3-9fc6-c213a60b3748_1313x875.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!8Ogd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F840d060e-f378-47d3-9fc6-c213a60b3748_1313x875.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>So how do general websites prevent data leakage?</strong></p><p>The usual approach is encryption. And this encryption may be performed multiple times at different stages. For example, common ones are: SHA256, salting, md5, RSA, etc..</p><p>This seems to be very safe, but there is still a problem. Developers know how to decrypt them.</p><p>Or some friends will think that the correct password still exists in the database? Once there is information leakage, won&#8217;t there still be a problem of password leakage?</p><p>Yes, so to solve this problem, in fact, the website itself doesn&#8217;t know what your password is.</p><h2><strong>The website itself doesn&#8217;t know what your password is</strong></h2><p>For websites (or other applications), they should not store your original password. Instead, they save your encrypted code through a series of operations. And this encryption is already performed when transmitted from the front end to the server, and it is an irreversible encryption operation, such as: MD5 + salting.</p><p>In general, we encrypt and save passwords through hash algorithms.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!m0bG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ca2bd5d-21b7-4e5a-b951-54c8cc7a17b0_1313x266.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!m0bG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ca2bd5d-21b7-4e5a-b951-54c8cc7a17b0_1313x266.png 424w, https://substackcdn.com/image/fetch/$s_!m0bG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ca2bd5d-21b7-4e5a-b951-54c8cc7a17b0_1313x266.png 848w, https://substackcdn.com/image/fetch/$s_!m0bG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ca2bd5d-21b7-4e5a-b951-54c8cc7a17b0_1313x266.png 1272w, https://substackcdn.com/image/fetch/$s_!m0bG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ca2bd5d-21b7-4e5a-b951-54c8cc7a17b0_1313x266.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!m0bG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ca2bd5d-21b7-4e5a-b951-54c8cc7a17b0_1313x266.png" width="1313" height="266" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2ca2bd5d-21b7-4e5a-b951-54c8cc7a17b0_1313x266.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:266,&quot;width&quot;:1313,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!m0bG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ca2bd5d-21b7-4e5a-b951-54c8cc7a17b0_1313x266.png 424w, https://substackcdn.com/image/fetch/$s_!m0bG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ca2bd5d-21b7-4e5a-b951-54c8cc7a17b0_1313x266.png 848w, https://substackcdn.com/image/fetch/$s_!m0bG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ca2bd5d-21b7-4e5a-b951-54c8cc7a17b0_1313x266.png 1272w, https://substackcdn.com/image/fetch/$s_!m0bG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ca2bd5d-21b7-4e5a-b951-54c8cc7a17b0_1313x266.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Hash algorithms, also known as hash functions or digest algorithms, are used to generate a fixed-length unique identifier for data of any length, also known as a hash value, or message digest (referred to as hash value later).</p><p>Currently, a more commonly used method is to encrypt passwords through MD5 + Salt. <mark data-color="rgb(232, 243, 232)" style="background-color: rgb(232, 243, 232); color: rgb(0, 0, 0);">In cryptography, salt refers to inserting a specific string at any fixed position in the password so that the result after hashing does not match the hashing result using the original password. This process is called &#8220;salting&#8221;.</mark></p><p>However, this method is no longer recommended because the MD5 algorithm has low security and poor collision resistance. You can use a more secure encrypted hash algorithm + Salt (salt) (such as SHA2, SHA3, SM3, with higher security and stronger collision resistance) or directly use slow hashing (such as Bcrypt).</p><p>Suppose we use this method of SHA-256 + Salt here.</p><p>Here is a simple sample code:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;450fc68e-8131-4d6c-9810-bdc7e1f27706&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">public class HashPasswordExample {
    public static void main(String[] args) {
        // Password to be hashed
        String password = &#8220;123456&#8221;;
        // Salt for hashing
        String salt = &#8220;salt-test&#8221;;
        try {
            // Create a SHA-256 digest object
            MessageDigest messageDigest = MessageDigest.getInstance(&#8221;SHA-256&#8221;);
            // Update the digest with the combined password and salt
            messageDigest.update((password + salt).getBytes());
            // Compute the hash value
            byte[] result = messageDigest.digest();
            // Convert the hash value to a hexadecimal string
            HexBinaryAdapter hexBinaryAdapter = new HexBinaryAdapter();
            String hexString = hexBinaryAdapter.marshal(result);
            // Print the original password
            System.out.println(&#8221;Original String: &#8220; + password);
            // Print the SHA-256 hash in lowercase
            System.out.println(&#8221;SHA-256 Hash: &#8220; + hexString.toLowerCase());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}</code></pre></div><p>Output:</p><pre><code><span>Original String: 123456
SHA-256 Hash: 3d5ecd90301b307c267f1c09e8a4b2433c2258fcb7441df91a32e245ea86bf96</span></code></pre><p>In this example, what the server saves is the data after salting and hashing the password &#8220;123456&#8221;, that is: &#8220;<code>3d5ecd90301b307c267f1c09e8a4b2433c2258fcb7441df91a32e245ea86bf96</code>&#8221;.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!4SOZ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3b4e9ed-899e-4acc-85c8-99b3212b5dfe_1313x310.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!4SOZ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3b4e9ed-899e-4acc-85c8-99b3212b5dfe_1313x310.png 424w, https://substackcdn.com/image/fetch/$s_!4SOZ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3b4e9ed-899e-4acc-85c8-99b3212b5dfe_1313x310.png 848w, https://substackcdn.com/image/fetch/$s_!4SOZ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3b4e9ed-899e-4acc-85c8-99b3212b5dfe_1313x310.png 1272w, https://substackcdn.com/image/fetch/$s_!4SOZ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3b4e9ed-899e-4acc-85c8-99b3212b5dfe_1313x310.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!4SOZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3b4e9ed-899e-4acc-85c8-99b3212b5dfe_1313x310.png" width="1313" height="310" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b3b4e9ed-899e-4acc-85c8-99b3212b5dfe_1313x310.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:310,&quot;width&quot;:1313,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!4SOZ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3b4e9ed-899e-4acc-85c8-99b3212b5dfe_1313x310.png 424w, https://substackcdn.com/image/fetch/$s_!4SOZ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3b4e9ed-899e-4acc-85c8-99b3212b5dfe_1313x310.png 848w, https://substackcdn.com/image/fetch/$s_!4SOZ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3b4e9ed-899e-4acc-85c8-99b3212b5dfe_1313x310.png 1272w, https://substackcdn.com/image/fetch/$s_!4SOZ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb3b4e9ed-899e-4acc-85c8-99b3212b5dfe_1313x310.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>When you enter your password to log in, the server will first take out the salt corresponding to your password, and then perform the process of obtaining the hash value again. If the finally calculated hash value is the same as the hash value saved in the database, it means that the password is correct. Otherwise, the password is incorrect.</p><p>The hash algorithm is irreversible. You cannot obtain the original value from the hashed value. In this way, the server does not know what your original password is, and naturally it cannot tell you what your original password is.</p><h2><strong>Why do many websites require that the changed password cannot be the same as the original password?</strong></h2><p>Then some friends have doubts again. Why do many websites require that the changed password cannot be the same as the original password? How is this implemented?</p><p>This process is actually the same as verifying the correctness of the password. Just calculate the hash value and compare it!</p><div><hr></div><p>Okay, that&#8217;s all for this time, see you next time! &#129325;</p><p><strong>Finally, if the article was helpful, please clap &#128079;and follow, thank you! &#9584;(*&#176;&#9661;&#176;*)&#9583;</strong></p><p><strong>I&#8217;m Dylan, looking forward to progressing with you. &#10084;&#65039;</strong></p>]]></content:encoded></item><item><title><![CDATA[Inteview: Why do You Need to Use Message Queue?]]></title><description><![CDATA[Why asynchronous architecture speeds up systems, what trade-offs it introduces, and how to decide if you actually need it]]></description><link>https://seabreeze0828.substack.com/p/inteview-why-do-you-need-to-use-message</link><guid isPermaLink="false">https://seabreeze0828.substack.com/p/inteview-why-do-you-need-to-use-message</guid><dc:creator><![CDATA[Dylan Smith]]></dc:creator><pubDate>Sat, 15 Aug 2026 15:19:22 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!La_n!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1de31efa-c508-4678-a8ce-8ec30d846912_1313x763.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!La_n!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1de31efa-c508-4678-a8ce-8ec30d846912_1313x763.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!La_n!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1de31efa-c508-4678-a8ce-8ec30d846912_1313x763.png 424w, https://substackcdn.com/image/fetch/$s_!La_n!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1de31efa-c508-4678-a8ce-8ec30d846912_1313x763.png 848w, https://substackcdn.com/image/fetch/$s_!La_n!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1de31efa-c508-4678-a8ce-8ec30d846912_1313x763.png 1272w, https://substackcdn.com/image/fetch/$s_!La_n!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1de31efa-c508-4678-a8ce-8ec30d846912_1313x763.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!La_n!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1de31efa-c508-4678-a8ce-8ec30d846912_1313x763.png" width="1313" height="763" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1de31efa-c508-4678-a8ce-8ec30d846912_1313x763.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:763,&quot;width&quot;:1313,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!La_n!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1de31efa-c508-4678-a8ce-8ec30d846912_1313x763.png 424w, https://substackcdn.com/image/fetch/$s_!La_n!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1de31efa-c508-4678-a8ce-8ec30d846912_1313x763.png 848w, https://substackcdn.com/image/fetch/$s_!La_n!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1de31efa-c508-4678-a8ce-8ec30d846912_1313x763.png 1272w, https://substackcdn.com/image/fetch/$s_!La_n!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1de31efa-c508-4678-a8ce-8ec30d846912_1313x763.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://seabreeze0828.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">The Java Knowledge Ocean is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>Let me give you an example that everyone can understand.&#128559;</p><p>Grace is Tom&#8217;s older sister.</p><p>Lately, Tom has been obsessed with cartoons, while Grace hopes that he watches more documentaries, so she often looks for high-quality documentaries for Tom to watch. Initially, Grace would ask Tom when he was free, give him the documentary, and personally supervise him until he finished watching before leaving. Over time, both of them felt inconvenient.</p><p>After that, the way changed: Grace told Tom, &#8220;All the documentaries on the video shelf need to be watched.&#8221; Since then, whenever Grace finds a good documentary, she will put it on the video shelf, and Tom will take it down and watch it when he sees a documentary on the shelf.</p><p>The video shelf is equivalent to a <strong>message queue</strong>, and Grace is the producer, and Tom is the consumer. The benefits of this are:</p><ol><li><p>Grace doesn&#8217;t need to ask Tom when he is free or hand him the documentary herself. She just needs to put it on the video shelf, which gives both parties more freedom of time.</p></li><li><p>Grace believes in Tom&#8217;s self-discipline and viewing ability, and doesn&#8217;t need to supervise his viewing process. She just needs to place the documentary, which saves a lot of time.</p></li><li><p>When other friends who like to watch documentaries join, Grace still only needs to put the documentary on the video shelf, and both Tom and they can pick it up from the video shelf assuming that when multiple people pick up the same documentary at the same time, they can all get a copy.</p></li><li><p>The documentary is placed on the video shelf, and Tom can watch it at his own pace. It doesn&#8217;t matter if he finishes it early or late. Compared to Grace&#8217;s delivery and supervision method, Tom will feel less stressed.</p></li></ol><h2><strong>Here are the four benefits of message queues:&#128077;</strong></h2><h3><strong>1.Decoupling</strong></h3><p>Each member (Grace and Tom) is not affected by other members and can be more independent. They only <mark data-color="rgb(232, 243, 232)" style="background-color: rgb(232, 243, 232); color: rgb(0, 0, 0);">connect through a simple container </mark>(the video shelf). Grace may not even know who takes the documentary from the video shelf, and Tom may not know who puts the documentary on the video shelf. In their eyes, there is only the video shelf, not the other person. There is no doubt that dealing with a simple container is a hundred times easier than dealing with a complicated person. Grace and Tom can freely pursue their own preferences.</p><h3><strong>2.Speeding up</strong></h3><p>By choosing to &#8220;place the documentary on the video shelf and not ask about anything else&#8221;, Grace saves a lot of time. Although Grace is very busy, it only takes a few minutes to place the documentary on the video shelf.</p><h3><strong>3.Broadcasting</strong></h3><p>Grace only needs to make one effort to allow multiple friends to see the documentary, which saves her a lot of time and reduces the cost of new friends joining.</p><h3><strong>4.Traffic peak shaving</strong></h3><p>If Tom watches the documentary slowly, the method of Grace supervising the viewing will bring pressure to Tom and also make Grace impatient. Since Grace&#8217;s frequency of providing documentaries is unstable, if she provides five consecutive ones in the next two days and only one in the next three months, then Tom only needs to take and watch the five documentaries from the video shelf within three months, without watching them immediately, which will reduce the pressure a lot.</p><h2><strong>Of course, using message queues comes at a cost:&#128532;</strong></h2><h3><strong>1.Introduced complexity</strong></h3><p>There is no doubt that the &#8220;video cabinet&#8221; is <mark data-color="rgb(232, 243, 232)" style="background-color: rgb(232, 243, 232); color: rgb(0, 0, 0);">an</mark> extra thing that needs to be placed and needs to be protected from theft.</p><h3><strong>2.Temporary inconsistency</strong></h3><p>If Mom asks Grace, &#8220;What documentaries has Tom watched recently?&#8221; In the old way, Grace can tell Mom the answer with confidence because she personally supervised Tom watching the documentary. But in the new way, Grace will think to herself, &#8220;Tom should finish watching it soon&#8230;&#8221; There is a period of time where &#8220;Mom thinks Tom has watched a certain documentary, but Tom hasn&#8217;t actually watched it yet.&#8221; Of course, Tom&#8217;s final viewing status will be consistent with Mom&#8217;s perception, which is what is meant by &#8220;eventual consistency.&#8221;</p><h2><strong>So, under what conditions should message queues be used? &#129300;</strong></h2><h3><strong>1.The producer does not need feedback from the consumer.</strong></h3><p>Before the introduction of a message queue, the return value of the interface should be empty, which makes it possible for the lower-level actions to not yet be completed, but the upper-level thinks that the actions have been completed and continues to move forward &#8212; that is, so-called asynchronous.<br>After Grace puts the documentary, she doesn&#8217;t ask whether Tom has watched it or not. She assumes that he has watched it, otherwise she would have to use the original method to supervise until he finishes watching it.</p><h3><strong>2.Allow temporary inconsistencies.</strong></h3><p>Mom might find that &#8220;sometimes it is said that Tom has watched a certain documentary, but in fact, he has not watched it yet.&#8221; As long as Mom is satisfied with &#8220;anyway, he watched it in the end&#8221;, asynchronous processing will be fine. If Mom cannot tolerate this situation and gets angry at Grace, Grace will not dare to use the video cabinet method.</p><h3><strong>3.Only use when it is effective and beneficial.</strong></h3><p>That said, the benefits of decoupling, speeding up, broadcasting, and peak clipping outweigh the cost of placing and monitoring the video cabinet. Otherwise, if you blindly copy, &#8220;I heard Jerry&#8217;s family bought a video cabinet, let&#8217;s get one too,&#8221; and you buy it and it doesn&#8217;t come in handy, you&#8217;ll only complicate the steps, and you might as well just give the documentary to the other side. This is not right. That is, don&#8217;t over-design.</p><div><hr></div><p>Okay, that&#8217;s all for this time, see you next time! &#129325;</p><p><strong>Finally, if the article was helpful, please clap &#128079;and follow, thank you! &#9584;(*&#176;&#9661;&#176;*)&#9583;</strong></p><p><strong>I&#8217;m Dylan, looking forward to progressing with you. &#10084;&#65039;</strong></p>]]></content:encoded></item><item><title><![CDATA[Distributed Rate Limiting, Explained: From a Boba Shop Queue to Production Java, 4 Classic Algorithms and a Complete Architecture Overview ]]></title><description><![CDATA[A complete guide covering 4 classic algorithms, runnable Jedis/Redisson/Gateway implementations, and high-concurrency pitfalls.]]></description><link>https://seabreeze0828.substack.com/p/distributed-rate-limiting-explained</link><guid isPermaLink="false">https://seabreeze0828.substack.com/p/distributed-rate-limiting-explained</guid><dc:creator><![CDATA[Dylan Smith]]></dc:creator><pubDate>Fri, 14 Aug 2026 16:19:25 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!vIb6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7999ca93-716f-448c-89ca-596ddc17c727_1206x314.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!vIb6!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7999ca93-716f-448c-89ca-596ddc17c727_1206x314.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!vIb6!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7999ca93-716f-448c-89ca-596ddc17c727_1206x314.png 424w, https://substackcdn.com/image/fetch/$s_!vIb6!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7999ca93-716f-448c-89ca-596ddc17c727_1206x314.png 848w, https://substackcdn.com/image/fetch/$s_!vIb6!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7999ca93-716f-448c-89ca-596ddc17c727_1206x314.png 1272w, https://substackcdn.com/image/fetch/$s_!vIb6!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7999ca93-716f-448c-89ca-596ddc17c727_1206x314.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!vIb6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7999ca93-716f-448c-89ca-596ddc17c727_1206x314.png" width="1206" height="314" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7999ca93-716f-448c-89ca-596ddc17c727_1206x314.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:314,&quot;width&quot;:1206,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:109082,&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://seabreeze0828.substack.com/i/211191572?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7999ca93-716f-448c-89ca-596ddc17c727_1206x314.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_!vIb6!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7999ca93-716f-448c-89ca-596ddc17c727_1206x314.png 424w, https://substackcdn.com/image/fetch/$s_!vIb6!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7999ca93-716f-448c-89ca-596ddc17c727_1206x314.png 848w, https://substackcdn.com/image/fetch/$s_!vIb6!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7999ca93-716f-448c-89ca-596ddc17c727_1206x314.png 1272w, https://substackcdn.com/image/fetch/$s_!vIb6!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7999ca93-716f-448c-89ca-596ddc17c727_1206x314.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Rate limiting is something every backend engineer eventually runs into. Product launches, new releases, scrapers, buggy callers. Any of them can push a system to its limits. This article skips theory for its own sake. Four algorithms are explained through a single boba shop, and the second half drops straight into Java code you can compile and run, plus the architecture diagrams.</p><p>Before writing it, I set myself four goals.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://seabreeze0828.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">The Java Knowledge Ocean is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><ul><li><p>All four algorithms explained through the same boba shop, so nothing gets introduced twice.</p></li><li><p>Three implementations you can compile and run, one each for Jedis, Redisson, and the gateway.</p></li><li><p>Ten Mermaid diagrams and five boba shop illustrations, so both the algorithms and the scenes have visuals.</p></li><li><p>Production pitfalls and selection advice you can actually decide with.</p></li></ul><p><strong>Conventions.</strong> The code targets Spring Boot 2.7.x, Redisson 3.23.x, Jedis 4.3.x, and Spring Cloud Gateway 2021.0.x. Everything compiles on Java 8 and newer, and the important lines have comments.</p><h2><strong>Chapter 1: Why you need rate limiting, starting with an API overload</strong></h2><p>A popular open source project ships a new release, and everyone upgrades within hours. CI retries, issue lookups, and status polling all hit the GitHub API at once, and traffic multiplies in a short window. Endpoints start timing out, third-party integrations fail, and the alert channel fills up. Any team that runs a public API recognizes this pattern. Stripe has an engineering blog post about rate limiters. GitHub&#8217;s API docs publish the hourly quota. AWS API Gateway has token bucket throttling built in. Google&#8217;s SRE workbook devotes a chapter to client-side throttling so servers do not get overwhelmed.</p><p>I have read this postmortem too many times. Afterward, everyone says the same thing. If only we had rate limiting.</p><p>Rate limiting is not a config flag you flip and forget. The algorithm, the code, and the architecture all matter, and production will remind you if any layer is wrong.</p><p><strong>Rate limiting is the last line of defense for a system.</strong> It does not make the system faster. It keeps overloaded requests out when pressure exceeds capacity, so the main services survive. Everything below follows from that.</p><h3><strong>1.1 How that traffic wave takes a system down</strong></h3><p>A typical API overload plays out like this.</p><ol><li><p>Traffic exceeds the API&#8217;s capacity, and the business thread pool starts queueing.</p></li><li><p>Slow requests fill the database connection pool, and new requests cannot get a connection.</p></li><li><p>Clients retry after timeouts, and the retries pile onto the same endpoints.</p></li><li><p>Dependent services time out one after another, and the outage spreads from the primary API to everything around it.</p></li><li><p>Scaling out does not help in time. New instances get flooded too.</p></li></ol><p>These incidents share a pattern. The system collapses after traffic exceeds capacity, and a code bug is usually just the trigger. Response times go from 20ms to 2s. Slow requests fill the thread pool, health checks start failing, the registry evicts instances, and traffic moves to the survivors, making things worse in a loop.</p><p>Each stage amplifies the one before it. Rate limiting stops the extra traffic at stage one.</p><h3><strong>1.2 The line between local and distributed rate limiting</strong></h3><p>Local rate limiting keeps its state inside a single JVM. Guava&#8217;s RateLimiter, a Semaphore, or an AtomicLong counter all count. With one instance it is simple, fast, and has no network cost. That breaks the moment you run several instances. Three instances each allowing 100 requests means 300 total, so the limit is meaningless.</p><p>Distributed rate limiting puts the state somewhere every instance shares, usually Redis. Each instance runs the same Lua script against the same key, so no matter how many machines are in the cluster, one threshold controls the total.</p><p>The rule of thumb is simple. Where does the state live? In JVM memory, it is local rate limiting. In shared storage like Redis, it is distributed. Neither is better, they just fit different situations.</p><p>Two conditions force you to go distributed.</p><ul><li><p>The service runs multiple instances, and the limit has to apply globally.</p></li><li><p>The quota is counted per user, account, or device, which a single machine cannot tally.</p></li></ul><p>Public API quotas, anti-abuse, and global risk control fall into the second group. Single-machine protection, local fallbacks, and local debugging can stay with local rate limiting.</p><h3><strong>1.3 What rate limiting actually protects</strong></h3><p>Everyone talks about smoothing out peaks. On a real system, rate limiting protects three things.</p><ul><li><p>Downstream capacity. Databases, third-party APIs, and message queues all have throughput ceilings. Leaky buckets and token buckets keep the input rate inside what they can handle.</p></li><li><p>Service SLA. Under heavy traffic the main endpoints still respond within the limit, so timeout and error rates stay controlled.</p></li><li><p>Failure boundaries. Overloaded requests stay outside the system instead of cascading into other services.</p></li></ul><p>Those three map to two places. The traffic entry point caps the total, and resource-facing layers such as databases absorb slow, sustained pressure.</p><p>The first protects resources, the second protects the experience, and the third protects everything around it.</p><h2><strong>Chapter 2: Four classic rate limiting algorithms, explained through everyday scenes</strong></h2><h3><strong>2.1 Meet the boba shop</strong></h3><p>Four algorithms explained separately tend to drift apart, so I put them all in one shop. The shop is called Boba Planet, on the ground floor of an office building, with a pink sign and white lettering that lights up around noon. The queue runs from the counter to the street corner. The owner hears nothing but order calls all day, and by the time the pearls are cooked there is no time to scoop them. He sets a rule, and when it stops working, he replaces it. The four algorithms are the four rules he went through, each one fixing a weakness of the last while introducing a new problem.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ec2J!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe00c74f-d7df-4bab-87c2-15d8613b5319_1672x1063.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ec2J!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe00c74f-d7df-4bab-87c2-15d8613b5319_1672x1063.png 424w, https://substackcdn.com/image/fetch/$s_!ec2J!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe00c74f-d7df-4bab-87c2-15d8613b5319_1672x1063.png 848w, https://substackcdn.com/image/fetch/$s_!ec2J!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe00c74f-d7df-4bab-87c2-15d8613b5319_1672x1063.png 1272w, https://substackcdn.com/image/fetch/$s_!ec2J!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe00c74f-d7df-4bab-87c2-15d8613b5319_1672x1063.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ec2J!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe00c74f-d7df-4bab-87c2-15d8613b5319_1672x1063.png" width="1456" height="926" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/be00c74f-d7df-4bab-87c2-15d8613b5319_1672x1063.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:926,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:88519,&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://seabreeze0828.substack.com/i/211191572?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe00c74f-d7df-4bab-87c2-15d8613b5319_1672x1063.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_!ec2J!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe00c74f-d7df-4bab-87c2-15d8613b5319_1672x1063.png 424w, https://substackcdn.com/image/fetch/$s_!ec2J!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe00c74f-d7df-4bab-87c2-15d8613b5319_1672x1063.png 848w, https://substackcdn.com/image/fetch/$s_!ec2J!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe00c74f-d7df-4bab-87c2-15d8613b5319_1672x1063.png 1272w, https://substackcdn.com/image/fetch/$s_!ec2J!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe00c74f-d7df-4bab-87c2-15d8613b5319_1672x1063.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The order of the rules is the order the algorithms evolved. First you solve whether there is a limit at all, then how accurate it is, then how smooth, then whether it can handle bursts.</p><h3><strong>2.2 Fixed window counter</strong></h3><h4><strong>2.2.1 The shop&#8217;s version</strong></h4><p>The first rule is the simplest. One hundred orders per hour. The barista clicks a counter with every order, and at 100 the shop puts up the full sign. The counter resets on the hour. A customer at 9:58 may be turned away, while someone at 10:00 on the dot can order immediately.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!GuOw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8789fce9-5b65-4718-a3c2-4b7e2192920f_1710x1042.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!GuOw!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8789fce9-5b65-4718-a3c2-4b7e2192920f_1710x1042.png 424w, https://substackcdn.com/image/fetch/$s_!GuOw!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8789fce9-5b65-4718-a3c2-4b7e2192920f_1710x1042.png 848w, https://substackcdn.com/image/fetch/$s_!GuOw!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8789fce9-5b65-4718-a3c2-4b7e2192920f_1710x1042.png 1272w, https://substackcdn.com/image/fetch/$s_!GuOw!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8789fce9-5b65-4718-a3c2-4b7e2192920f_1710x1042.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!GuOw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8789fce9-5b65-4718-a3c2-4b7e2192920f_1710x1042.png" width="1456" height="887" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8789fce9-5b65-4718-a3c2-4b7e2192920f_1710x1042.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:887,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:90168,&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://seabreeze0828.substack.com/i/211191572?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8789fce9-5b65-4718-a3c2-4b7e2192920f_1710x1042.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_!GuOw!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8789fce9-5b65-4718-a3c2-4b7e2192920f_1710x1042.png 424w, https://substackcdn.com/image/fetch/$s_!GuOw!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8789fce9-5b65-4718-a3c2-4b7e2192920f_1710x1042.png 848w, https://substackcdn.com/image/fetch/$s_!GuOw!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8789fce9-5b65-4718-a3c2-4b7e2192920f_1710x1042.png 1272w, https://substackcdn.com/image/fetch/$s_!GuOw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8789fce9-5b65-4718-a3c2-4b7e2192920f_1710x1042.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><h4><strong>2.2.2 The mechanics</strong></h4><p>A fixed window implementation ties the count to an expiration. One Redis key per window. Each request runs INCR, the first write sets a 60 second expiry, and once the count passes the threshold, requests are rejected. When the window ends, the key disappears and a new window starts. The whole thing is O(1) and uses one counter&#8217;s worth of memory.</p><p>Only two Redis commands are involved, INCR and EXPIRE. When the first INCR returns 1, set EXPIRE 60. The count keeps growing inside the window, and the key disappears when the window ends. In Lua the two steps can be merged so the expiry setup does not race with the first request.</p><h4><strong>2.2.3 The boundary spike</strong></h4><p>The problem is at the window boundary. The 100th order lands at 9:59:59, the counter resets at 10:00:00, and another 100 orders pass at 10:00:01. Two hundred orders in two seconds, twice the limit. The algorithm only knows windows, not time, so a burst that straddles the boundary makes the limit pointless.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_vsi!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c094177-05db-40ae-84f2-c74a59cf1e5c_1992x147.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_vsi!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c094177-05db-40ae-84f2-c74a59cf1e5c_1992x147.png 424w, https://substackcdn.com/image/fetch/$s_!_vsi!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c094177-05db-40ae-84f2-c74a59cf1e5c_1992x147.png 848w, https://substackcdn.com/image/fetch/$s_!_vsi!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c094177-05db-40ae-84f2-c74a59cf1e5c_1992x147.png 1272w, https://substackcdn.com/image/fetch/$s_!_vsi!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c094177-05db-40ae-84f2-c74a59cf1e5c_1992x147.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_vsi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c094177-05db-40ae-84f2-c74a59cf1e5c_1992x147.png" width="1456" height="107" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2c094177-05db-40ae-84f2-c74a59cf1e5c_1992x147.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:107,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:27014,&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://seabreeze0828.substack.com/i/211191572?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c094177-05db-40ae-84f2-c74a59cf1e5c_1992x147.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_!_vsi!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c094177-05db-40ae-84f2-c74a59cf1e5c_1992x147.png 424w, https://substackcdn.com/image/fetch/$s_!_vsi!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c094177-05db-40ae-84f2-c74a59cf1e5c_1992x147.png 848w, https://substackcdn.com/image/fetch/$s_!_vsi!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c094177-05db-40ae-84f2-c74a59cf1e5c_1992x147.png 1272w, https://substackcdn.com/image/fetch/$s_!_vsi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c094177-05db-40ae-84f2-c74a59cf1e5c_1992x147.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p></p><p>A fixed window only counts the current window and clears at the boundary. The 9:00 window and the 10:00 window each allow 100 orders, so the two seconds around the boundary have no window of their own. That is the boundary spike. A sliding window treats adjacent windows as one continuous span and closes the gap.</p><h4><strong>2.2.4 Trade-offs and when to use it</strong></h4><p>The upsides are simplicity, memory usage, and speed. The downside is that boundary traffic can double. It is good enough for SMS codes, login attempt limits, and basic abuse protection where a small overshoot is acceptable. For a public API that cares about the total, do not rely on it alone.</p><p>If the requirement is simply 100 orders per window with immediate rejection after that, a fixed window still works, as long as you accept the brief overshoot at the boundary.</p><h3><strong>2.3 Sliding window counter</strong></h3><h4><strong>2.3.1 The shop&#8217;s version</strong></h4><p>After seeing the boundary problem, the owner changes the rule. At most 100 orders in any continuous 60 minutes, not per clock hour. The barista splits the hour into six 10-minute slots, marks a tally in the matching slot for every order, and at any moment counts the last six slots. The window slides forward with the current time, so 9:59 and 10:01 fall inside the same window.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!x9rY!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F81b84aa4-949d-4038-af6e-00102b322523_1710x1041.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!x9rY!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F81b84aa4-949d-4038-af6e-00102b322523_1710x1041.png 424w, https://substackcdn.com/image/fetch/$s_!x9rY!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F81b84aa4-949d-4038-af6e-00102b322523_1710x1041.png 848w, https://substackcdn.com/image/fetch/$s_!x9rY!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F81b84aa4-949d-4038-af6e-00102b322523_1710x1041.png 1272w, https://substackcdn.com/image/fetch/$s_!x9rY!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F81b84aa4-949d-4038-af6e-00102b322523_1710x1041.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!x9rY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F81b84aa4-949d-4038-af6e-00102b322523_1710x1041.png" width="1456" height="886" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/81b84aa4-949d-4038-af6e-00102b322523_1710x1041.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:886,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:101507,&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://seabreeze0828.substack.com/i/211191572?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F81b84aa4-949d-4038-af6e-00102b322523_1710x1041.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_!x9rY!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F81b84aa4-949d-4038-af6e-00102b322523_1710x1041.png 424w, https://substackcdn.com/image/fetch/$s_!x9rY!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F81b84aa4-949d-4038-af6e-00102b322523_1710x1041.png 848w, https://substackcdn.com/image/fetch/$s_!x9rY!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F81b84aa4-949d-4038-af6e-00102b322523_1710x1041.png 1272w, https://substackcdn.com/image/fetch/$s_!x9rY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F81b84aa4-949d-4038-af6e-00102b322523_1710x1041.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><h4><strong>2.3.2 The mechanics</strong></h4><p>There are two common implementations. The first uses a sorted set. Each request&#8217;s timestamp becomes a member, ZREMRANGEBYSCORE removes everything outside the window, and ZCARD counts what is left. The second splits time into a fixed number of small counter keys, increments the slot each request lands in, and sums the last N slots when checking. Finer slots get closer to true sliding, at a higher memory and CPU cost.</p><p>The sorted set approach adds a member for every request, so the set grows with traffic. Production usually picks the second option and trades a little accuracy for a bounded cost.</p><h4><strong>2.3.3 Trade-offs and when to use it</strong></h4><p>It is accurate and gets rid of the boundary spike for good. The price is that every request leaves a trace, so memory and CPU costs are clearly higher. It suits login protection, endpoint scraping defense, and risk control rules where accuracy matters. At very large scale, teams usually layer local counters on top instead of sending every request to Redis.</p><p>A common starting point is 6 to 12 slots for a 60 second window. Going down to one second slots gets expensive fast.</p><h3><strong>2.4 Leaky bucket</strong></h3><h4><strong>2.4.1 The shop&#8217;s version</strong></h4><p>The third rule takes a different angle. The owner stops limiting how many orders are taken. A conveyor belt at the pickup counter delivers drinks at a steady pace, 20 cups per minute. Whether 20 or 200 people are waiting, the kitchen does not speed up. When the waiting area is full, new customers are turned away and told to come back.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!F1JX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c4f03bc-f39c-449d-a2b2-22af7efdc37d_1704x1036.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!F1JX!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c4f03bc-f39c-449d-a2b2-22af7efdc37d_1704x1036.png 424w, https://substackcdn.com/image/fetch/$s_!F1JX!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c4f03bc-f39c-449d-a2b2-22af7efdc37d_1704x1036.png 848w, https://substackcdn.com/image/fetch/$s_!F1JX!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c4f03bc-f39c-449d-a2b2-22af7efdc37d_1704x1036.png 1272w, https://substackcdn.com/image/fetch/$s_!F1JX!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c4f03bc-f39c-449d-a2b2-22af7efdc37d_1704x1036.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!F1JX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c4f03bc-f39c-449d-a2b2-22af7efdc37d_1704x1036.png" width="1456" height="885" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2c4f03bc-f39c-449d-a2b2-22af7efdc37d_1704x1036.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:885,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:75432,&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://seabreeze0828.substack.com/i/211191572?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c4f03bc-f39c-449d-a2b2-22af7efdc37d_1704x1036.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_!F1JX!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c4f03bc-f39c-449d-a2b2-22af7efdc37d_1704x1036.png 424w, https://substackcdn.com/image/fetch/$s_!F1JX!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c4f03bc-f39c-449d-a2b2-22af7efdc37d_1704x1036.png 848w, https://substackcdn.com/image/fetch/$s_!F1JX!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c4f03bc-f39c-449d-a2b2-22af7efdc37d_1704x1036.png 1272w, https://substackcdn.com/image/fetch/$s_!F1JX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2c4f03bc-f39c-449d-a2b2-22af7efdc37d_1704x1036.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h4><strong>2.4.2 The mechanics</strong></h4><p>A leaky bucket drops requests into a bucket with a fixed drain rate. An arriving request enters if there is room and is rejected if the bucket is full. Requests flow out at a constant rate and get consumed by the backend. The implementation only tracks two values, the current water level and the last drain time, and each request computes how much water leaked based on the elapsed time.</p><p>The drain is all about elapsed time. Ten seconds between two requests means ten seconds&#8217; worth of water leaks out, and the longer the gap, the emptier the bucket. This is the mirror image of a token bucket refilling tokens.</p><h4><strong>2.4.3 Trade-offs and when to use it</strong></h4><p>The output is perfectly smooth. The downstream always sees a constant rate, with no bursts at all. The trade-off is that bursts have no chance. Even if the system built up spare capacity while idle, it cannot release it at peak. It fits databases, third-party APIs, and message consumers that demand a stable rate.</p><h3><strong>2.5 Token bucket</strong></h3><h4><strong>2.5.1 The shop&#8217;s version</strong></h4><p>The fourth rule finally handles bursts. The kitchen produces 20 bright yellow pickup tokens per minute and drops them into a bucket that holds at most 40. A customer grabs a token to pick up a drink, immediately if one is available, and waits for a fresh token otherwise. When traffic is light, tokens pile up. When the lunch rush hits, the first wave can spend the 40 accumulated tokens at once, while the kitchen refills at 20 per minute.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!uWpO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a19b1c3-c9e5-46ac-aaf1-b710693792b2_1715x1039.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!uWpO!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a19b1c3-c9e5-46ac-aaf1-b710693792b2_1715x1039.png 424w, https://substackcdn.com/image/fetch/$s_!uWpO!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a19b1c3-c9e5-46ac-aaf1-b710693792b2_1715x1039.png 848w, https://substackcdn.com/image/fetch/$s_!uWpO!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a19b1c3-c9e5-46ac-aaf1-b710693792b2_1715x1039.png 1272w, https://substackcdn.com/image/fetch/$s_!uWpO!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a19b1c3-c9e5-46ac-aaf1-b710693792b2_1715x1039.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!uWpO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a19b1c3-c9e5-46ac-aaf1-b710693792b2_1715x1039.png" width="1456" height="882" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7a19b1c3-c9e5-46ac-aaf1-b710693792b2_1715x1039.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:882,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:99143,&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://seabreeze0828.substack.com/i/211191572?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a19b1c3-c9e5-46ac-aaf1-b710693792b2_1715x1039.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_!uWpO!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a19b1c3-c9e5-46ac-aaf1-b710693792b2_1715x1039.png 424w, https://substackcdn.com/image/fetch/$s_!uWpO!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a19b1c3-c9e5-46ac-aaf1-b710693792b2_1715x1039.png 848w, https://substackcdn.com/image/fetch/$s_!uWpO!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a19b1c3-c9e5-46ac-aaf1-b710693792b2_1715x1039.png 1272w, https://substackcdn.com/image/fetch/$s_!uWpO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7a19b1c3-c9e5-46ac-aaf1-b710693792b2_1715x1039.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><h4><strong>2.5.2 The mechanics</strong></h4><p>A token bucket has two parameters, the refill rate and the bucket capacity. Tokens are produced at a fixed rate, and the capacity caps how many can accumulate. A request consumes a token, passes if one is available, and is rejected or waits if not. Compared with a leaky bucket, the extra ability to save tokens is where bursts come from. The implementation keeps two values, the token count and the last refill time, which is a bit more work than a counter.</p><p>A common starting point for tuning is simple. Set rate to what the downstream can sustain, and set capacity to the largest burst the business allows. Capacity must be above 1, or the token bucket degrades into a leaky bucket.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!J61X!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee905cc8-031f-45f0-ba4c-3bf31c3eeb87_1718x312.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!J61X!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee905cc8-031f-45f0-ba4c-3bf31c3eeb87_1718x312.png 424w, https://substackcdn.com/image/fetch/$s_!J61X!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee905cc8-031f-45f0-ba4c-3bf31c3eeb87_1718x312.png 848w, https://substackcdn.com/image/fetch/$s_!J61X!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee905cc8-031f-45f0-ba4c-3bf31c3eeb87_1718x312.png 1272w, https://substackcdn.com/image/fetch/$s_!J61X!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee905cc8-031f-45f0-ba4c-3bf31c3eeb87_1718x312.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!J61X!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee905cc8-031f-45f0-ba4c-3bf31c3eeb87_1718x312.png" width="1456" height="264" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ee905cc8-031f-45f0-ba4c-3bf31c3eeb87_1718x312.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:264,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:43664,&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://seabreeze0828.substack.com/i/211191572?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee905cc8-031f-45f0-ba4c-3bf31c3eeb87_1718x312.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_!J61X!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee905cc8-031f-45f0-ba4c-3bf31c3eeb87_1718x312.png 424w, https://substackcdn.com/image/fetch/$s_!J61X!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee905cc8-031f-45f0-ba4c-3bf31c3eeb87_1718x312.png 848w, https://substackcdn.com/image/fetch/$s_!J61X!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee905cc8-031f-45f0-ba4c-3bf31c3eeb87_1718x312.png 1272w, https://substackcdn.com/image/fetch/$s_!J61X!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee905cc8-031f-45f0-ba4c-3bf31c3eeb87_1718x312.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p></p><p>Tokens refill at a fixed rate and the capacity sets the ceiling. When a request arrives, the bucket is checked first. Enough tokens means a deduction and a pass. Otherwise the request is rejected or waits. Saved tokens from idle periods are what make bursts possible, and that is the biggest difference from a leaky bucket.</p><h4><strong>2.5.3 Trade-offs and when to use it</strong></h4><p>It balances smoothness and bursts. The rate is capped, and short spikes can reach the capacity ceiling. The risk lives in the capacity parameter. Set it too high and a spike still overwhelms the downstream. Public APIs, gateway entrances, and most business rate limiting use it, which makes it the most common choice in production.</p><p>In practice, the most common token bucket implementation is Redis Lua, and all three implementations in Chapter 3 use it.</p><h3><strong>2.6 Side-by-side comparison and how to choose</strong></h3><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!tcJL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faca59330-0297-42e9-b7d7-9ca0aab2add9_1638x263.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!tcJL!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faca59330-0297-42e9-b7d7-9ca0aab2add9_1638x263.png 424w, https://substackcdn.com/image/fetch/$s_!tcJL!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faca59330-0297-42e9-b7d7-9ca0aab2add9_1638x263.png 848w, https://substackcdn.com/image/fetch/$s_!tcJL!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faca59330-0297-42e9-b7d7-9ca0aab2add9_1638x263.png 1272w, https://substackcdn.com/image/fetch/$s_!tcJL!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faca59330-0297-42e9-b7d7-9ca0aab2add9_1638x263.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!tcJL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faca59330-0297-42e9-b7d7-9ca0aab2add9_1638x263.png" width="1456" height="234" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/aca59330-0297-42e9-b7d7-9ca0aab2add9_1638x263.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:234,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:63389,&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://seabreeze0828.substack.com/i/211191572?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faca59330-0297-42e9-b7d7-9ca0aab2add9_1638x263.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_!tcJL!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faca59330-0297-42e9-b7d7-9ca0aab2add9_1638x263.png 424w, https://substackcdn.com/image/fetch/$s_!tcJL!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faca59330-0297-42e9-b7d7-9ca0aab2add9_1638x263.png 848w, https://substackcdn.com/image/fetch/$s_!tcJL!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faca59330-0297-42e9-b7d7-9ca0aab2add9_1638x263.png 1272w, https://substackcdn.com/image/fetch/$s_!tcJL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faca59330-0297-42e9-b7d7-9ca0aab2add9_1638x263.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>My default is the token bucket. It controls both rate and bursts and covers most business scenarios. Pick the leaky bucket when smoothness is a hard requirement, the sliding window when accuracy is, and the fixed window when the only goal is cheap scraping protection.</p><p>Start with what you are protecting. Databases and abuse protection are different problems, and no single algorithm wins everywhere.</p><h3><strong>2.7 A question to think about</strong></h3><blockquote><p>Question Why do public APIs like Stripe and the GitHub API almost always use a token bucket instead of a leaky bucket?</p></blockquote><p>Callers of public APIs routinely burst. CI reruns, data syncs, and batch jobs can fire dozens of requests in the same second. A leaky bucket flattens that wave entirely, clients wait a long time, and timeout retries make it worse. A token bucket lets callers spend accumulated tokens at once, so part of the first peak gets through while the average rate stays capped by rate. Stripe&#8217;s engineering post on rate limiter design uses the token bucket as the main algorithm. Rate limiting keeps traffic inside what the system can handle. Short peaks are allowed, sustained overload is not. The same logic applies to thresholds. Size capacity for the peak and rate for the average, and the wider the gap between them, the more jitter the system absorbs.</p><h2><strong>Chapter 3: Distributed rate limiting in Java, production code you can compile and run</strong></h2><h3><strong>3.1 Why atomicity comes first</strong></h3><p>Rate limiting logic has three steps. Read the state, decide, deduct. When Java writes them as separate operations, races are unavoidable. Two requests both read one remaining token, both decide they can pass, and both go through. The limit fails on the spot.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ta5H!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e8e3780-3551-47f7-acca-16ff9a064a8f_1831x682.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ta5H!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e8e3780-3551-47f7-acca-16ff9a064a8f_1831x682.png 424w, https://substackcdn.com/image/fetch/$s_!ta5H!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e8e3780-3551-47f7-acca-16ff9a064a8f_1831x682.png 848w, https://substackcdn.com/image/fetch/$s_!ta5H!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e8e3780-3551-47f7-acca-16ff9a064a8f_1831x682.png 1272w, https://substackcdn.com/image/fetch/$s_!ta5H!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e8e3780-3551-47f7-acca-16ff9a064a8f_1831x682.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ta5H!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e8e3780-3551-47f7-acca-16ff9a064a8f_1831x682.png" width="1456" height="542" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3e8e3780-3551-47f7-acca-16ff9a064a8f_1831x682.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:542,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:55761,&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://seabreeze0828.substack.com/i/211191572?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e8e3780-3551-47f7-acca-16ff9a064a8f_1831x682.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_!ta5H!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e8e3780-3551-47f7-acca-16ff9a064a8f_1831x682.png 424w, https://substackcdn.com/image/fetch/$s_!ta5H!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e8e3780-3551-47f7-acca-16ff9a064a8f_1831x682.png 848w, https://substackcdn.com/image/fetch/$s_!ta5H!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e8e3780-3551-47f7-acca-16ff9a064a8f_1831x682.png 1272w, https://substackcdn.com/image/fetch/$s_!ta5H!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3e8e3780-3551-47f7-acca-16ff9a064a8f_1831x682.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Java splits the read, the decision, and the write into three Redis calls, so two instances can interleave and both see the stale value. Lua merges the three steps into one atomic execution, and a request that arrives later only ever sees the deducted result. That is what keeps distributed rate limiting correct.</p><p>A lock works on a single machine, but a JVM lock cannot stop another machine. A distributed lock is slow, heavy, and adds a new failure point. Redis solves this with Lua. Redis executes commands on a single thread, and when EVAL runs a script, the whole script completes atomically with no other command interleaved. Put the read, the refill, the decision, and the deduction in one script, and the race is gone.</p><p>There are two side benefits. One round trip runs the whole check, which beats a chain of GETs and SETs. And the logic lives on the Redis side, so the Java layer does not have to implement concurrency control.</p><p>For contrast, WATCH and MULTI can achieve atomicity with optimistic locking, but conflicting requests have to retry, and the retry rate explodes at peak. Lua has no retry cost. The script runs and the result is final. That is why mainstream rate limiting components all use Lua.</p><h3><strong>3.2 Implementation 1: plain Java, Redis, and a Lua token bucket</strong></h3><p>This option only depends on Jedis and does not tie you to a framework. It fits a self-built middleware or a project sensitive about dependencies.</p><h4><strong>3.2.1 Maven dependencies</strong></h4><p>Jedis 4.3.1, JUnit 5.9.2 for tests, and the Maven compiler targets Java 8.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;xml&quot;,&quot;nodeId&quot;:&quot;52c799b8-df12-4417-aa72-aa3f67391797&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-xml">&lt;project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"&gt;
    &lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;

    &lt;groupId&gt;com.example&lt;/groupId&gt;
    &lt;artifactId&gt;ratelimit-jedis&lt;/artifactId&gt;
    &lt;version&gt;1.0.0&lt;/version&gt;
    &lt;packaging&gt;jar&lt;/packaging&gt;

    &lt;properties&gt;
        &lt;maven.compiler.source&gt;8&lt;/maven.compiler.source&gt;
        &lt;maven.compiler.target&gt;8&lt;/maven.compiler.target&gt;
        &lt;project.build.sourceEncoding&gt;UTF-8&lt;/project.build.sourceEncoding&gt;
        &lt;jedis.version&gt;4.3.1&lt;/jedis.version&gt;
        &lt;junit.version&gt;5.9.2&lt;/junit.version&gt;
    &lt;/properties&gt;

    &lt;dependencies&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;redis.clients&lt;/groupId&gt;
            &lt;artifactId&gt;jedis&lt;/artifactId&gt;
            &lt;version&gt;${jedis.version}&lt;/version&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.slf4j&lt;/groupId&gt;
            &lt;artifactId&gt;slf4j-api&lt;/artifactId&gt;
            &lt;version&gt;1.7.36&lt;/version&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.slf4j&lt;/groupId&gt;
            &lt;artifactId&gt;slf4j-simple&lt;/artifactId&gt;
            &lt;version&gt;1.7.36&lt;/version&gt;
            &lt;scope&gt;test&lt;/scope&gt;
        &lt;/dependency&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.junit.jupiter&lt;/groupId&gt;
            &lt;artifactId&gt;junit-jupiter&lt;/artifactId&gt;
            &lt;version&gt;${junit.version}&lt;/version&gt;
            &lt;scope&gt;test&lt;/scope&gt;
        &lt;/dependency&gt;
    &lt;/dependencies&gt;

    &lt;build&gt;
        &lt;plugins&gt;
            &lt;plugin&gt;
                &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
                &lt;artifactId&gt;maven-surefire-plugin&lt;/artifactId&gt;
                &lt;version&gt;3.0.0&lt;/version&gt;
            &lt;/plugin&gt;
        &lt;/plugins&gt;
    &lt;/build&gt;
&lt;/project&gt;
</code></pre></div><h4><strong>3.2.2 The Lua script</strong></h4><p>Put the script below at <code>src/main/resources/lua/token_bucket.lua</code>. Refilling, deciding, and deducting all happen inside one EVAL, which is where the atomicity comes from.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;e736617e-fa9b-4a5d-92e1-c9e0645ee930&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash">-- Token bucket rate limiting script
-- KEYS[1]  bucket key, a hash that stores the token count in 'tokens' and the last refill time in 'ts'
-- ARGV[1]  bucket capacity, the maximum number of tokens that can accumulate
-- ARGV[2]  refill rate in tokens per second
-- ARGV[3]  tokens consumed by this request
-- Returns 1 to allow, 0 to reject

local key = KEYS[1]
local capacity = tonumber(ARGV[1])
local rate = tonumber(ARGV[2])
local requested = tonumber(ARGV[3])

-- Use the Redis server time to avoid clock drift on client machines
local t = redis.call('time')
local now = tonumber(t[1])

local tokens = tonumber(redis.call('hget', key, 'tokens') or capacity)
local last = tonumber(redis.call('hget', key, 'ts') or now)

local elapsed = now - last
if elapsed &gt; 0 then
    tokens = tokens + elapsed * rate
    if tokens &gt; capacity then
        tokens = capacity
    end
end

-- Write back the refilled token count whether the request passes or not
redis.call('hset', key, 'tokens', tokens, 'ts', now)

if tokens &gt;= requested then
    redis.call('hset', key, 'tokens', tokens - requested, 'ts', now)
    return 1
end

return 0
</code></pre></div><p>The script deliberately reads the time from the Redis server instead of accepting a client-supplied timestamp. Skewed clocks across application servers cannot affect the decision. The script uses the TIME command, and since Redis 5.0 scripts replicate by effects, the rate-limiting state stays consistent after a failover.</p><p>The argument order is fixed. The caller passes capacity, rate, and requested, and the script only computes. That keeps one script reusable across endpoints, with the key separating business dimensions.</p><h4><strong>3.2.3 The Java utility class</strong></h4><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;6432466b-a16a-4ef1-ba27-984e0c3cb9f0&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">package com.example.ratelimit;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import redis.clients.jedis.JedisPooled;
import redis.clients.jedis.exceptions.JedisException;
import redis.clients.jedis.exceptions.JedisNoScriptException;

/**
 * Distributed token bucket rate limiter built on Jedis.
 * All state lives in Redis, so multiple instances share one bucket.
 *
 * @author Dylan
 */
public class TokenBucketRateLimiter implements AutoCloseable {

    private static final Logger log = LoggerFactory.getLogger(TokenBucketRateLimiter.class);

    private static final String LUA_PATH = "/lua/token_bucket.lua";

    private static final String LUA_SCRIPT = loadScript(LUA_PATH);

    private final JedisPooled jedis;

    /** Whether to allow requests when Redis fails. true keeps the business available, false rejects to protect the downstream. */
    private final boolean failOpen;

    /** SHA of the Lua script, loaded with SCRIPT LOAD on first use. */
    private volatile String scriptSha;

    public TokenBucketRateLimiter(JedisPooled jedis) {
        this(jedis, true);
    }

    public TokenBucketRateLimiter(JedisPooled jedis, boolean failOpen) {
        this.jedis = jedis;
        this.failOpen = failOpen;
    }

    /**
     * Try to acquire permits tokens.
     *
     * @param key      the rate limit dimension, for example order:create:9527
     * @param capacity bucket capacity
     * @param rate     tokens refilled per second
     * @param permits  tokens needed by this request
     * @return true to allow, false to reject
     */
    public boolean tryAcquire(String key, double capacity, double rate, int permits) {
        try {
            List&lt;String&gt; keys = Collections.singletonList(key);
            List&lt;String&gt; args = Arrays.asList(
                    String.valueOf(capacity),
                    String.valueOf(rate),
                    String.valueOf(permits));
            Object result = eval(keys, args);
            return Long.parseLong(result.toString()) == 1L;
        } catch (JedisException ex) {
            log.error("Rate limiting failed, applying the fallback policy, key={}", key, ex);
            if (failOpen) {
                return true;
            }
            throw new RateLimitUnavailableException("Rate limiter unavailable", ex);
        }
    }

    private Object eval(List&lt;String&gt; keys, List&lt;String&gt; args) {
        if (scriptSha == null) {
            scriptSha = jedis.scriptLoad(LUA_SCRIPT);
        }
        try {
            return jedis.evalsha(scriptSha, keys, args);
        } catch (JedisNoScriptException ex) {
            // The script cache is lost when Redis restarts, so reload and run once more
            scriptSha = jedis.scriptLoad(LUA_SCRIPT);
            return jedis.evalsha(scriptSha, keys, args);
        }
    }

    private static String loadScript(String path) {
        try (InputStream in = TokenBucketRateLimiter.class.getResourceAsStream(path)) {
            if (in == null) {
                throw new IllegalStateException("Lua script not found: " + path);
            }
            StringBuilder sb = new StringBuilder();
            try (BufferedReader reader = new BufferedReader(
                    new InputStreamReader(in, StandardCharsets.UTF_8))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    sb.append(line).append('\n');
                }
            }
            return sb.toString();
        } catch (IOException ex) {
            throw new IllegalStateException("Failed to read the Lua script", ex);
        }
    }

    @Override
    public void close() {
        jedis.close();
    }
}
</code></pre></div><p>Two details worth knowing. The script is loaded once with SCRIPT LOAD and then executed with EVALSHA, which saves sending the script on every call. Redis can drop its script cache on restart, so JedisNoScriptException is caught and the script is reloaded once.</p><p>The companion exception class follows.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;7c4fbdc8-79e9-4208-aedd-ec92c3d43902&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">package com.example.ratelimit;

/** Thrown when the rate limiter is unavailable, used in fail-closed scenarios. */
public class RateLimitUnavailableException extends RuntimeException {

    public RateLimitUnavailableException(String message, Throwable cause) {
        super(message, cause);
    }
}
</code></pre></div><h4><strong>3.2.4 Unit tests</strong></h4><p>The tests need a local Redis at 127.0.0.1, port 6379. The first test fires 100 concurrent requests against a bucket with capacity 10 and expects exactly 10 to pass. The second verifies that tokens refill over time.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;c4f96323-dad1-4050-b8e0-fba4a67aee6a&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">package com.example.ratelimit;

import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

import redis.clients.jedis.JedisPooled;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

class TokenBucketRateLimiterTest {

    private static final String HOST = "127.0.0.1";
    private static final int PORT = 6379;
    private static final String KEY_PREFIX = "test:token-bucket:";

    private static JedisPooled jedis;
    private static TokenBucketRateLimiter limiter;

    @BeforeAll
    static void setUp() {
        jedis = new JedisPooled(HOST, PORT);
        limiter = new TokenBucketRateLimiter(jedis, true);
    }

    @AfterAll
    static void tearDown() {
        jedis.close();
    }

    @Test
    void concurrentRequestsShouldBeLimitedByCapacity() throws Exception {
        String key = KEY_PREFIX + UUID.randomUUID();
        int total = 100;
        int capacity = 10;
        // Keep the refill rate tiny during the test so only the initial tokens are available
        double rate = 0.001;

        ExecutorService pool = Executors.newFixedThreadPool(total);
        CountDownLatch ready = new CountDownLatch(total);
        CountDownLatch start = new CountDownLatch(1);
        AtomicInteger passed = new AtomicInteger();
        AtomicInteger rejected = new AtomicInteger();

        for (int i = 0; i &lt; total; i++) {
            pool.submit(() -&gt; {
                ready.countDown();
                try {
                    start.await();
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    return;
                }
                if (limiter.tryAcquire(key, capacity, rate, 1)) {
                    passed.incrementAndGet();
                } else {
                    rejected.incrementAndGet();
                }
            });
        }

        assertTrue(ready.await(10, TimeUnit.SECONDS), "threads did not become ready");
        start.countDown();
        pool.shutdown();
        assertTrue(pool.awaitTermination(30, TimeUnit.SECONDS), "tasks did not finish in time");

        assertEquals(10, passed.get(), "only 10 of 100 concurrent requests should get tokens");
        assertEquals(90, rejected.get(), "the other 90 requests should be rejected");
        jedis.del(key);
    }

    @Test
    void tokensShouldRefillOverTime() throws Exception {
        String key = KEY_PREFIX + UUID.randomUUID();
        int capacity = 2;
        double rate = 1.0;

        assertTrue(limiter.tryAcquire(key, capacity, rate, 2), "the first request should pass");
        assertFalse(limiter.tryAcquire(key, capacity, rate, 1), "should reject once the tokens run out");
        Thread.sleep(1100);
        assertTrue(limiter.tryAcquire(key, capacity, rate, 1), "should refill tokens after 1.1 seconds");
        jedis.del(key);
    }
}
</code></pre></div><h4><strong>3.2.5 How to run it and verify it works</strong></h4><p><strong>How to run it.</strong></p><ol><li><p>Start Redis. <code>docker run -d -p 6379:6379 --name redis7 redis:7-alpine</code></p></li><li><p>Put the pom.xml, the Lua script, and the two Java classes into a Maven project, keeping the directory layout.</p></li><li><p>Run <code>mvn test</code>.</p></li></ol><p><strong>How to verify it.</strong> Watch the test output. The concurrency test passes 10 and rejects 90, and the refill test passes again after waiting 1.1 seconds. Both assertions passing means the rate limiting works.</p><h3><strong>3.3 Implementation 2: Redisson</strong></h3><p>Redisson wraps the token bucket in RRateLimiter, which is the fastest option to wire into a production project.</p><h4><strong>3.3.1 Dependencies and configuration</strong></h4><p>Spring Boot 2.7.x pairs with Redisson 3.23.x. Matching the versions matters, because a mismatch shows up as class loading errors at startup.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;xml&quot;,&quot;nodeId&quot;:&quot;ce887343-f120-4399-8315-c9362934075c&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-xml">&lt;dependency&gt;
    &lt;groupId&gt;org.redisson&lt;/groupId&gt;
    &lt;artifactId&gt;redisson-spring-boot-starter&lt;/artifactId&gt;
    &lt;version&gt;3.23.5&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
    &lt;artifactId&gt;spring-boot-starter-aop&lt;/artifactId&gt;
&lt;/dependency&gt;
</code></pre></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;ee16cef7-e2e4-49df-bd5b-cb30b5b3203a&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">spring:
  application:
    name: ratelimit-redisson
  redis:
    host: 127.0.0.1
    port: 6379
    database: 0
</code></pre></div><p>The starter exposes a RedissonClient bean, and both the utility class and the aspect get their limiter from it.</p><p>Redisson&#8217;s limiter is also Lua under the hood, with the script and connection management wrapped up. rate and rateInterval combine into a speed, for example rate=20, rateInterval=1, unit=SECONDS means 20 tokens per second.</p><h4><strong>3.3.2 Calling it through a utility class</strong></h4><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;dc6266a4-c5f4-4479-820e-3421b7b44136&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">package com.example.ratelimit.redisson;

import java.util.concurrent.TimeUnit;

import javax.annotation.Resource;

import org.redisson.api.RRateLimiter;
import org.redisson.api.RateIntervalUnit;
import org.redisson.api.RateType;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;

/**
 * Wraps the initialization and acquisition of RRateLimiter.
 *
 * @author Dylan
 */
@Component
public class RedissonRateLimiterUtil {

    @Resource
    private RedissonClient redissonClient;

    /** Non-blocking acquisition, returns false immediately when no token is available. */
    public boolean tryAcquire(String key, long rate, long rateInterval,
                              RateIntervalUnit unit, int permits) {
        RRateLimiter rateLimiter = redissonClient.getRateLimiter(key);
        // The config is only written on the first call, already configured keys are skipped
        rateLimiter.trySetRate(RateType.OVERALL, rate, rateInterval, unit);
        return rateLimiter.tryAcquire(permits);
    }

    /** Acquisition with a wait time, blocking the current thread while waiting. */
    public boolean tryAcquire(String key, long rate, long rateInterval,
                              RateIntervalUnit unit, int permits,
                              long timeout, TimeUnit timeUnit) {
        RRateLimiter rateLimiter = redissonClient.getRateLimiter(key);
        rateLimiter.trySetRate(RateType.OVERALL, rate, rateInterval, unit);
        return rateLimiter.tryAcquire(permits, timeout, timeUnit);
    }
}
</code></pre></div><p>Two things to keep in mind. trySetRate only applies when the key does not exist yet, so changing a threshold means deleting the old key and setting it again. RRateLimiter keys do not expire on their own, so watch Redis memory over the long run.</p><h4><strong>3.3.3 Calling it through an annotation</strong></h4><p>First define the annotation. The key supports SpEL placeholders such as <code>order:create:#userId</code>, and the annotation itself carries no logic, only the dimension and the rate.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;dc6d8bdb-0aa1-4ba8-8a4c-84e27dd5b0aa&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">package com.example.ratelimit.redisson;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.redisson.api.RateIntervalUnit;

/** Method-level distributed rate limiting annotation. */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimit {

    /** Rate limit key, supports #parameterName placeholders */
    String key();

    /** Rate value */
    long rate() default 10;

    /** Rate window length */
    long rateInterval() default 1;

    /** Rate window unit */
    RateIntervalUnit unit() default RateIntervalUnit.SECONDS;

    /** Tokens consumed per call */
    int permits() default 1;
}
</code></pre></div><p>Next comes the aspect. It resolves the key, initializes the limiter, and performs the decision. The aspect binds the annotation with @Around, grabs a token before the method runs, and throws when it cannot.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;afd09f53-8a18-4591-b7b0-0c9a6a52a52e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">package com.example.ratelimit.redisson;

import javax.annotation.Resource;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RRateLimiter;
import org.redisson.api.RateType;
import org.redisson.api.RedissonClient;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

/**
 * Resolves the rate limit key from the annotation and method arguments,
 * then runs the token acquisition uniformly.
 *
 * @author Dylan
 */
@Aspect
@Component
public class RateLimitAspect {

    private static final ExpressionParser PARSER = new SpelExpressionParser();
    private static final TemplateParserContext TEMPLATE = new TemplateParserContext();

    @Resource
    private RedissonClient redissonClient;

    @Around("@annotation(rateLimit)")
    public Object around(ProceedingJoinPoint joinPoint, RateLimit rateLimit) throws Throwable {
        String key = resolveKey(rateLimit.key(), joinPoint);
        RRateLimiter rateLimiter = redissonClient.getRateLimiter(key);
        rateLimiter.trySetRate(RateType.OVERALL, rateLimit.rate(), rateLimit.rateInterval(), rateLimit.unit());
        if (rateLimiter.tryAcquire(rateLimit.permits())) {
            return joinPoint.proceed();
        }
        throw new RateLimitException("Too many requests, please try again later");
    }

    private String resolveKey(String template, ProceedingJoinPoint joinPoint) {
        if (template.contains("#")) {
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            StandardEvaluationContext context = new StandardEvaluationContext();
            String[] paramNames = signature.getParameterNames();
            Object[] args = joinPoint.getArgs();
            if (paramNames != null) {
                for (int i = 0; i &lt; paramNames.length; i++) {
                    context.setVariable(paramNames[i], args[i]);
                }
            }
            return PARSER.parseExpression(template, TEMPLATE).getValue(context, String.class);
        }
        return template;
    }
}
</code></pre></div><p>The companion exception follows. It extends RuntimeException, so the business layer can catch it and return a unified error code.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;0c67da00-7e67-42f9-b59a-171d79ef0ec5&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">package com.example.ratelimit.redisson;

public class RateLimitException extends RuntimeException {

    public RateLimitException(String message) {
        super(message);
    }
}
</code></pre></div><p>Here is the annotation in a business method. The key uses the #userId placeholder, and the aspect pulls the real user ID from the method arguments.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;b13c98ed-48ba-4e5a-89e7-7ead9e9bfee7&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">package com.example.ratelimit.service;

import org.redisson.api.RateIntervalUnit;
import org.springframework.stereotype.Service;

import com.example.ratelimit.redisson.RateLimit;

@Service
public class OrderService {

    @RateLimit(key = "order:create:#userId", rate = 20, rateInterval = 1,
            unit = RateIntervalUnit.SECONDS, permits = 1)
    public Order createOrder(Long userId, OrderCreateRequest request) {
        // normal business logic
        return new Order();
    }
}
</code></pre></div><p>If getParameterNames() returns null, add the <code>-parameters</code> flag to the compiler plugin so the aspect can resolve <code>#userId</code>.</p><h4><strong>3.3.4 Notes for cluster environments</strong></h4><p>Cluster mode keeps its configuration in a separate file that application.yml points to.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;6462b075-2464-4105-bc4f-3f1828227718&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">spring:
  redis:
    redisson:
      file: classpath:redisson-cluster.yaml
</code></pre></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;6576b4ed-8356-4d74-b17d-97b394cc431c&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">clusterServersConfig:
  nodeAddresses:
    - "redis://10.0.0.11:6379"
    - "redis://10.0.0.12:6379"
    - "redis://10.0.0.13:6379"
  scanInterval: 1000
  timeout: 3000
  retryAttempts: 2
  retryInterval: 1500
</code></pre></div><p>A few things deserve attention in cluster mode. Use RateType.OVERALL so the whole cluster shares one bucket, because PER_CLIENT only makes sense on a single machine. Prefix keys with the business dimension, for example <code>rate:order:create:9527</code>, so endpoints do not pollute each other. Keep the Redis timeout short. The rate limiter must not slow down the main flow, and timeouts should fall through to the fallback policy.</p><p>Sentinel and Cluster have different configuration layouts, and a migration should also check that the script keys land in the same slot. Single-key scripts are unaffected. Multi-key scripts must use hash tags to keep their keys together.</p><h4><strong>3.3.5 How to run it and verify it works</strong></h4><p><strong>How to run it.</strong></p><ol><li><p>Start Redis, add the dependencies, and configure application.yml.</p></li><li><p>Inject RedissonRateLimiterUtil, or annotate a method with @RateLimit.</p></li><li><p>Start the application and call the endpoint repeatedly.</p></li></ol><p><strong>How to verify it.</strong> Set rate to 1 with a 1 second window. Fire requests within one second. The first passes and the rest return RateLimitException or false. Wait one second, request again, and it passes again.</p><h3><strong>3.4 Implementation 3: Spring Cloud Gateway</strong></h3><h4><strong>3.4.1 Why put rate limiting at the gateway</strong></h4><p>The gateway is the single entry point for traffic. Rate limiting there means zero changes to business code. Rejected requests never reach the services, which saves resources across the entire call path. The rules stay in one place and can combine route, IP, and user dimensions freely.</p><p>The cost is worth knowing too. Every request pays one Redis round trip, so the limiter itself becomes a cap on the gateway&#8217;s QPS. Account for it during capacity planning.</p><h4><strong>3.4.2 Dependencies and basic configuration</strong></h4><p>Spring Boot 2.7.18 with Spring Cloud 2021.0.8.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;xml&quot;,&quot;nodeId&quot;:&quot;7bf9d05d-e565-4225-a919-773d05f261c9&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-xml">&lt;parent&gt;
    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
    &lt;artifactId&gt;spring-boot-starter-parent&lt;/artifactId&gt;
    &lt;version&gt;2.7.18&lt;/version&gt;
&lt;/parent&gt;

&lt;properties&gt;
    &lt;spring-cloud.version&gt;2021.0.8&lt;/spring-cloud.version&gt;
&lt;/properties&gt;

&lt;dependencies&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
        &lt;artifactId&gt;spring-cloud-starter-gateway&lt;/artifactId&gt;
    &lt;/dependency&gt;
    &lt;dependency&gt;
        &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
        &lt;artifactId&gt;spring-boot-starter-data-redis-reactive&lt;/artifactId&gt;
    &lt;/dependency&gt;
&lt;/dependencies&gt;

&lt;dependencyManagement&gt;
    &lt;dependencies&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
            &lt;artifactId&gt;spring-cloud-dependencies&lt;/artifactId&gt;
            &lt;version&gt;${spring-cloud.version}&lt;/version&gt;
            &lt;type&gt;pom&lt;/type&gt;
            &lt;scope&gt;import&lt;/scope&gt;
        &lt;/dependency&gt;
    &lt;/dependencies&gt;
&lt;/dependencyManagement&gt;
</code></pre></div><p>Here is the route configuration. replenishRate is the number of tokens refilled per second, burstCapacity is the bucket capacity, and requestedTokens is how many tokens each request consumes.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;1955c154-5372-4303-b50c-00f7348b214c&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">spring:
  application:
    name: gateway
  redis:
    host: 127.0.0.1
    port: 6379
  cloud:
    gateway:
      routes:
        - id: order-service
          uri: lb://order-service
          predicates:
            - Path=/order/**
          filters:
            - name: CustomRequestRateLimiter
              args:
                key-resolver: "#{@ipKeyResolver}"
                redis-rate-limiter.replenishRate: 10
                redis-rate-limiter.burstCapacity: 20
                redis-rate-limiter.requestedTokens: 1
</code></pre></div><p>The three parameters map straight back to the boba shop. replenishRate is how many tokens the kitchen makes per minute, burstCapacity is the bucket size, and requestedTokens is what a customer spends to pick up one drink.</p><h4><strong>3.4.3 KeyResolver: choosing the rate limit dimension</strong></h4><p>The built-in RedisRateLimiter needs to know which dimension to limit on. The bean below limits by client IP. In production you would usually switch to a user ID or an endpoint plus user combination.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;2bdbbc15-2f89-424a-b094-21df9aa1f076&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">package com.example.gateway.limiter;

import java.util.Objects;

import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import reactor.core.publisher.Mono;

@Configuration
public class RateLimitConfig {

    /** Limits by client IP. Production usually combines a user ID or endpoint dimension. */
    @Bean
    public KeyResolver ipKeyResolver() {
        return exchange -&gt; {
            String ip = Objects.requireNonNull(exchange.getRequest().getRemoteAddress())
                    .getAddress().getHostAddress();
            return Mono.just(ip);
        };
    }
}
</code></pre></div><h4><strong>3.4.4 Customizing the rate limit response</strong></h4><p>When the built-in filter rejects a request, it commits an empty 429 response and callers get no message. Returning a consistent JSON body takes two steps. First make the filter throw instead of committing the response, then let a unified exception handler render the JSON.</p><p>Start with the custom filter. It replicates the built-in decision flow and only changes the rejection path to throw a ResponseStatusException. The class name determines the filter name in the yml, so CustomRequestRateLimiterGatewayFilterFactory maps to CustomRequestRateLimiter.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;5951b27f-bf08-4733-9021-ec9a6bb3b20b&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">package com.example.gateway.limiter;

import java.util.Map;

import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.RequestRateLimiterGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.cloud.gateway.filter.ratelimit.RateLimiter;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ResponseStatusException;

import reactor.core.publisher.Mono;

/**
 * The built-in rate limiter commits a bare 429 response when it rejects a request,
 * so callers receive no body. This subclass changes the rejection path to throw,
 * and a unified exception handler renders the JSON.
 *
 * @author Dylan
 */
@Component
public class CustomRequestRateLimiterGatewayFilterFactory
        extends RequestRateLimiterGatewayFilterFactory {

    public CustomRequestRateLimiterGatewayFilterFactory(RateLimiter rateLimiter, KeyResolver keyResolver) {
        super(rateLimiter, keyResolver);
    }

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -&gt; {
            KeyResolver resolver = config.getKeyResolver() != null
                    ? config.getKeyResolver() : getDefaultKeyResolver();
            RateLimiter&lt;?&gt; limiter = config.getRateLimiter() != null
                    ? config.getRateLimiter() : getDefaultRateLimiter();

            return resolver.resolve(exchange).defaultIfEmpty("").flatMap(key -&gt; {
                if (key.isEmpty()) {
                    if (isDenyEmptyKey()) {
                        return Mono.error(
                                new ResponseStatusException(config.getStatusCode(), "empty key"));
                    }
                    return chain.filter(exchange);
                }
                String routeId = config.getRouteId();
                if (routeId == null) {
                    Route route = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
                    routeId = route != null ? route.getId() : "";
                }
                String finalRouteId = routeId;
                return limiter.isAllowed(finalRouteId, key).flatMap(response -&gt; {
                    for (Map.Entry&lt;String, String&gt; header : response.getHeaders().entrySet()) {
                        exchange.getResponse().getHeaders().add(header.getKey(), header.getValue());
                    }
                    if (response.isAllowed()) {
                        return chain.filter(exchange);
                    }
                    return Mono.error(
                            new ResponseStatusException(config.getStatusCode(), "rate limit exceeded"));
                });
            });
        };
    }
}
</code></pre></div><p>Then the exception handler. It only takes over for 429, and rethrows everything else to the framework&#8217;s default handling.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;91e74d5e-84c6-40f6-86d0-f5f47ca189fb&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">package com.example.gateway.limiter;

import java.nio.charset.StandardCharsets;

import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.server.ServerWebExchange;

import reactor.core.publisher.Mono;

/**
 * Renders rate limit exceptions as a uniform JSON body.
 *
 * @author Dylan
 */
@Component
@Order(Ordered.HIGHEST_PRECEDENCE + 1)
public class RateLimitErrorWebExceptionHandler implements ErrorWebExceptionHandler {

    @Override
    public Mono&lt;Void&gt; handle(ServerWebExchange exchange, Throwable ex) {
        if (ex instanceof ResponseStatusException) {
            ResponseStatusException rse = (ResponseStatusException) ex;
            if (HttpStatus.TOO_MANY_REQUESTS.equals(rse.getStatus())) {
                exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
                exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);
                String body = "{\"code\":429,\"message\":\"Too many requests, please try again later\"}";
                DataBuffer buffer = exchange.getResponse().bufferFactory()
                        .wrap(body.getBytes(StandardCharsets.UTF_8));
                return exchange.getResponse().writeWith(Mono.just(buffer));
            }
        }
        return Mono.error(ex);
    }
}
</code></pre></div><h4><strong>3.4.5 How to run it and verify it works</strong></h4><p><strong>How to run it.</strong></p><ol><li><p>Add the gateway and reactive Redis dependencies and configure the route.</p></li><li><p>Put the KeyResolver, the custom filter, and the exception handler into the project.</p></li><li><p>Start the gateway and hit the limited route repeatedly.</p></li></ol><p><strong>How to verify it.</strong> Run <code>curl -i</code> 30 times in a row. The first 20 return the business result, and from request 21 the gateway returns HTTP 429 with the JSON body. The response headers also show the token bucket state, such as X-RateLimit-Remaining.</p><h3><strong>3.5 For comparison: Guava local rate limiting</strong></h3><p>Guava&#8217;s RateLimiter is a plain local token bucket and the simplest code in this article, which makes the local versus distributed difference easy to see. The demo uses Guava 32.1.3-jre, and the class is called DylanRateLimiterDemo. Run main and watch what happens.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;xml&quot;,&quot;nodeId&quot;:&quot;66a65c64-6732-446e-9eb8-51b926ef3b3c&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-xml">&lt;dependency&gt;
    &lt;groupId&gt;com.google.guava&lt;/groupId&gt;
    &lt;artifactId&gt;guava&lt;/artifactId&gt;
    &lt;version&gt;32.1.3-jre&lt;/version&gt;
&lt;/dependency&gt;
</code></pre></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;9681dedf-391d-443a-890b-f98ec9d567f7&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">package com.example.ratelimit;

import com.google.common.util.concurrent.RateLimiter;

/**
 * Local token bucket demo.
 *
 * @author Dylan
 */
public class DylanRateLimiterDemo {

    public static void main(String[] args) {
        // Refills 10 tokens per second, and the bucket capacity defaults to 10
        RateLimiter rateLimiter = RateLimiter.create(10.0);

        for (int i = 1; i &lt;= 15; i++) {
            if (rateLimiter.tryAcquire()) {
                System.out.println("Request " + i + " allowed");
            } else {
                System.out.println("Request " + i + " rejected");
            }
        }
    }
}
</code></pre></div><p>When you run it, the first 10 requests pass and the rest are rejected. But it only works inside this JVM. With three instances behind a gateway, the total becomes 30. When a global quota is required, local rate limiting can only act as a fallback, not the primary mechanism.</p><h3><strong>3.6 Choosing among the three distributed implementations</strong></h3><p>There is no single right answer. Here is a conservative combination I would start with.</p><ul><li><p>Use Spring Cloud Gateway at the entry for coarse, endpoint-level limiting that blocks most abnormal traffic.</p></li><li><p>Use Redisson inside services for precise, user-level limiting, with flexible rules and low integration cost.</p></li><li><p>Build your own with Jedis and Lua when you need a custom algorithm or want to avoid a framework, with full control over the logic.</p></li><li><p>Keep Guava for single machine scenarios and local fallbacks.</p></li></ul><p>The combination follows one pattern. The closer a layer is to the traffic, the coarser its limiting. The closer it is to the business, the finer. Coarse layers chase speed, fine layers chase accuracy.</p><h2><strong>Chapter 4: Architecture design for distributed rate limiting, diagrams included</strong></h2><p>All four diagrams are written in Mermaid and render directly in Markdown. Each one comes with a short walkthrough.</p><h3><strong>4.1 Layered rate limiting architecture</strong></h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_xly!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf70d37d-d87c-4c89-9041-5993d23f0f2f_1947x865.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_xly!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf70d37d-d87c-4c89-9041-5993d23f0f2f_1947x865.png 424w, https://substackcdn.com/image/fetch/$s_!_xly!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf70d37d-d87c-4c89-9041-5993d23f0f2f_1947x865.png 848w, https://substackcdn.com/image/fetch/$s_!_xly!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf70d37d-d87c-4c89-9041-5993d23f0f2f_1947x865.png 1272w, https://substackcdn.com/image/fetch/$s_!_xly!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf70d37d-d87c-4c89-9041-5993d23f0f2f_1947x865.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_xly!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf70d37d-d87c-4c89-9041-5993d23f0f2f_1947x865.png" width="1456" height="647" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bf70d37d-d87c-4c89-9041-5993d23f0f2f_1947x865.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:647,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:108205,&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://seabreeze0828.substack.com/i/211191572?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf70d37d-d87c-4c89-9041-5993d23f0f2f_1947x865.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_!_xly!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf70d37d-d87c-4c89-9041-5993d23f0f2f_1947x865.png 424w, https://substackcdn.com/image/fetch/$s_!_xly!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf70d37d-d87c-4c89-9041-5993d23f0f2f_1947x865.png 848w, https://substackcdn.com/image/fetch/$s_!_xly!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf70d37d-d87c-4c89-9041-5993d23f0f2f_1947x865.png 1272w, https://substackcdn.com/image/fetch/$s_!_xly!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbf70d37d-d87c-4c89-9041-5993d23f0f2f_1947x865.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Traffic gets filtered layer by layer from the edge. Nginx applies coarse connection and IP level limits. The gateway limits by endpoint with a token bucket. Services then limit precisely by user. Each layer is configured independently, the coarse layers absorb the bulk, and the fine layers handle the details. All rate limiting state lives in the Redis cluster, so service instances stay stateless. The monitoring platform collects allowed counts, rejected counts, RT, and error rates, and pushes dynamic thresholds down to the gateway and services. The fallback module handles fail fast, queueing, and fallback data.</p><p>The point is that every layer limits, but each with a different granularity. As the granularity tightens, the room for error shrinks.</p><h3><strong>4.2 Token bucket execution flow</strong></h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Eeu1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba1dda8f-9497-4d8c-ab08-bd2221eb8105_1507x1166.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Eeu1!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba1dda8f-9497-4d8c-ab08-bd2221eb8105_1507x1166.png 424w, https://substackcdn.com/image/fetch/$s_!Eeu1!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba1dda8f-9497-4d8c-ab08-bd2221eb8105_1507x1166.png 848w, https://substackcdn.com/image/fetch/$s_!Eeu1!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba1dda8f-9497-4d8c-ab08-bd2221eb8105_1507x1166.png 1272w, https://substackcdn.com/image/fetch/$s_!Eeu1!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba1dda8f-9497-4d8c-ab08-bd2221eb8105_1507x1166.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Eeu1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba1dda8f-9497-4d8c-ab08-bd2221eb8105_1507x1166.png" width="1456" height="1127" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ba1dda8f-9497-4d8c-ab08-bd2221eb8105_1507x1166.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1127,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:79378,&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://seabreeze0828.substack.com/i/211191572?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba1dda8f-9497-4d8c-ab08-bd2221eb8105_1507x1166.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_!Eeu1!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba1dda8f-9497-4d8c-ab08-bd2221eb8105_1507x1166.png 424w, https://substackcdn.com/image/fetch/$s_!Eeu1!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba1dda8f-9497-4d8c-ab08-bd2221eb8105_1507x1166.png 848w, https://substackcdn.com/image/fetch/$s_!Eeu1!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba1dda8f-9497-4d8c-ab08-bd2221eb8105_1507x1166.png 1272w, https://substackcdn.com/image/fetch/$s_!Eeu1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fba1dda8f-9497-4d8c-ab08-bd2221eb8105_1507x1166.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Inside the Lua script, the request first reads the token count and the last refill time, refills tokens by the elapsed time, and then decides. The whole flow runs atomically on Redis&#8217;s single thread, so no concurrent request can interleave and read stale state. A return of 1 allows the request, 0 rejects it, and the client does not need a second check. Time comes from the Redis server, so clock drift on application machines cannot affect the decision.</p><p>The only condition is whether the refilled token count is at least the number requested. Even a rejection writes the state back, so the next request always reads the latest value.</p><h3><strong>4.3 Rate limit fallback flow</strong></h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Lngb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa1216139-1ebd-45da-9a9d-3ff73a4716ef_2336x631.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Lngb!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa1216139-1ebd-45da-9a9d-3ff73a4716ef_2336x631.png 424w, https://substackcdn.com/image/fetch/$s_!Lngb!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa1216139-1ebd-45da-9a9d-3ff73a4716ef_2336x631.png 848w, https://substackcdn.com/image/fetch/$s_!Lngb!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa1216139-1ebd-45da-9a9d-3ff73a4716ef_2336x631.png 1272w, https://substackcdn.com/image/fetch/$s_!Lngb!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa1216139-1ebd-45da-9a9d-3ff73a4716ef_2336x631.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Lngb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa1216139-1ebd-45da-9a9d-3ff73a4716ef_2336x631.png" width="1456" height="393" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a1216139-1ebd-45da-9a9d-3ff73a4716ef_2336x631.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:393,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:97420,&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://seabreeze0828.substack.com/i/211191572?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa1216139-1ebd-45da-9a9d-3ff73a4716ef_2336x631.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_!Lngb!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa1216139-1ebd-45da-9a9d-3ff73a4716ef_2336x631.png 424w, https://substackcdn.com/image/fetch/$s_!Lngb!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa1216139-1ebd-45da-9a9d-3ff73a4716ef_2336x631.png 848w, https://substackcdn.com/image/fetch/$s_!Lngb!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa1216139-1ebd-45da-9a9d-3ff73a4716ef_2336x631.png 1272w, https://substackcdn.com/image/fetch/$s_!Lngb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa1216139-1ebd-45da-9a9d-3ff73a4716ef_2336x631.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Once a request is limited, the chosen strategy depends on the business. Fail fast returns 429 directly. Queueing suits traffic shaping. Fallback data suits read-heavy workloads. A circuit breaker fits when the downstream is already failing. Redis failure takes a separate branch, where a degradation switch decides between fail-open and fail-closed. The switch lives in the config center, so it can change without a release, and traffic returns to Redis rate limiting automatically once Redis recovers.</p><p>Policies can be set per endpoint. Checkout fails fast, exports queue, and reads fall back to cache.</p><h3><strong>4.4 High availability deployment topology</strong></h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!RPiP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5cdb41c-acc6-42e0-a4fb-acd606acff33_1586x580.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!RPiP!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5cdb41c-acc6-42e0-a4fb-acd606acff33_1586x580.png 424w, https://substackcdn.com/image/fetch/$s_!RPiP!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5cdb41c-acc6-42e0-a4fb-acd606acff33_1586x580.png 848w, https://substackcdn.com/image/fetch/$s_!RPiP!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5cdb41c-acc6-42e0-a4fb-acd606acff33_1586x580.png 1272w, https://substackcdn.com/image/fetch/$s_!RPiP!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5cdb41c-acc6-42e0-a4fb-acd606acff33_1586x580.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!RPiP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5cdb41c-acc6-42e0-a4fb-acd606acff33_1586x580.png" width="1456" height="532" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d5cdb41c-acc6-42e0-a4fb-acd606acff33_1586x580.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:532,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:46935,&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://seabreeze0828.substack.com/i/211191572?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5cdb41c-acc6-42e0-a4fb-acd606acff33_1586x580.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_!RPiP!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5cdb41c-acc6-42e0-a4fb-acd606acff33_1586x580.png 424w, https://substackcdn.com/image/fetch/$s_!RPiP!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5cdb41c-acc6-42e0-a4fb-acd606acff33_1586x580.png 848w, https://substackcdn.com/image/fetch/$s_!RPiP!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5cdb41c-acc6-42e0-a4fb-acd606acff33_1586x580.png 1272w, https://substackcdn.com/image/fetch/$s_!RPiP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd5cdb41c-acc6-42e0-a4fb-acd606acff33_1586x580.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Service instances are stateless and all rate limiting state sits in Redis, so instances scale out freely. Redis runs in sentinel or cluster mode, and a master failure fails over automatically without losing the limiting state. Each instance only connects to Redis and does not depend on other instances, so one failing instance does not affect anyone else&#8217;s decisions. The monitoring platform watches Redis latency and replication state to catch capacity problems early.</p><p>Size for the worst case. Leave Redis QPS headroom for the rate limiting peak, and include replication lag in the monitoring.</p><h2><strong>Chapter 5: Production pitfalls and good practices</strong></h2><h3><strong>5.1 The pitfalls that show up most often</strong></h3><p><strong>A single point of failure in Redis.</strong> Rate limiting depends on Redis, and when Redis goes down the limiting goes with it. Teams without a fallback end up either allowing everything or rejecting everything, and neither is controlled. Decide the fallback policy during design, not during the incident. The fix is in section 5.2.</p><p><strong>Clock drift.</strong> If the script refills tokens using client time, a fast or slow application server shifts the decision. Use the Redis server time everywhere, and in multi region deployments watch the NTP sync on the Redis machines. When NTP drifts, the limiting clock and the business clock disagree, and log timestamps stop lining up.</p><p><strong>Hot keys.</strong> Popular campaigns, bestsellers, and hot open source repositories all funnel requests onto a single key, and one Redis shard becomes the bottleneck. The usual fixes are layering local limiting under distributed limiting, or sharding the key by business so each shard carries its own quota. Keep the shard count modest. Eight shards are usually enough, and more than that starts costing accuracy.</p><p><strong>Granularity that is too coarse or too fine.</strong> One global key means a single abusive user exhausts the limit for everyone. One key per user explodes memory and can be bypassed with throwaway accounts. A two-level combination works well, endpoint level for the total and user level for each user. The two keys would look like <code>rate:api:order:create</code> and <code>rate:user:9527:order:create</code>.</p><p><strong>Killing legitimate requests.</strong> A threshold picked from thin air will hurt normal traffic the moment it fluctuates. Base the threshold on historical peak statistics, keep tuning against the rejection and false positive rates, and run load tests against the plan before a launch or campaign. Track the false positive rate itself. It is the first number I look at after a rate limiter ships.</p><h3><strong>5.2 High availability options</strong></h3><p><strong>Redis high availability.</strong> Production should run at least sentinel mode, and cluster mode for critical traffic. All rate limiting state is read and written on the primary, with sentinel handling the failover.</p><p>Failover has a short window of unavailability, so test what the limiter does during that window too.</p><p><strong>Kill switches.</strong> Keep three switches in the config center. A master switch turns rate limiting on or off. A degradation switch controls behavior after a Redis failure. A whitelist switch lets internal systems and load test traffic through.</p><p>Stripe&#8217;s engineering post on running rate limiters in production stresses the same two things. Fail open when Redis fails so the API does not go down with it, and keep a kill switch so the limiter can be disabled in one action. That matches the switch design above.</p><p><strong>Multi-layer fallbacks.</strong> Nginx coarse limiting, gateway token buckets, and service local limiting stack into three layers. If one fails, the others keep working.</p><p><strong>Local standby.</strong> Each instance carries a local token bucket and switches to it automatically when Redis fails. The per-instance value is the total divided by the instance count, so accuracy drops, but the system is never running without a limit.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!uaCc!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F304a998e-03c7-4554-931e-b99e58a79a9a_1615x725.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!uaCc!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F304a998e-03c7-4554-931e-b99e58a79a9a_1615x725.png 424w, https://substackcdn.com/image/fetch/$s_!uaCc!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F304a998e-03c7-4554-931e-b99e58a79a9a_1615x725.png 848w, https://substackcdn.com/image/fetch/$s_!uaCc!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F304a998e-03c7-4554-931e-b99e58a79a9a_1615x725.png 1272w, https://substackcdn.com/image/fetch/$s_!uaCc!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F304a998e-03c7-4554-931e-b99e58a79a9a_1615x725.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!uaCc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F304a998e-03c7-4554-931e-b99e58a79a9a_1615x725.png" width="1456" height="654" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/304a998e-03c7-4554-931e-b99e58a79a9a_1615x725.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:654,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:57409,&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://seabreeze0828.substack.com/i/211191572?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F304a998e-03c7-4554-931e-b99e58a79a9a_1615x725.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_!uaCc!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F304a998e-03c7-4554-931e-b99e58a79a9a_1615x725.png 424w, https://substackcdn.com/image/fetch/$s_!uaCc!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F304a998e-03c7-4554-931e-b99e58a79a9a_1615x725.png 848w, https://substackcdn.com/image/fetch/$s_!uaCc!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F304a998e-03c7-4554-931e-b99e58a79a9a_1615x725.png 1272w, https://substackcdn.com/image/fetch/$s_!uaCc!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F304a998e-03c7-4554-931e-b99e58a79a9a_1615x725.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The limiter first checks whether Redis is available. If it is, the distributed path runs. On failure or timeout, it switches to the local token bucket immediately. The local threshold is the total divided by the instance count, less accurate but still a limit. When Redis recovers, it switches back, and callers never notice.</p><h3><strong>5.3 Monitoring and tuning</strong></h3><p>Track four metrics. Requests allowed, requests rejected, rejection rate, and limiter latency. A sudden jump in rejection rate usually means the threshold is too low. A jump in latency means Redis or the network has a problem. Export the metrics to Prometheus and set alert rules per endpoint tier.</p><p>If the limiter takes more than 10ms, investigate. It should stay under 1ms in normal conditions.</p><p>Do not guess alert thresholds. When allowed traffic drops and rejections spike at the same time, the limiter is almost certainly hurting legitimate users. Check the whitelist and dynamic thresholds first, then Redis latency.</p><p>Dynamic thresholds live in the config center. Keep them conservative day to day, raise them per the load test results before a launch or campaign, and roll back when it ends. Log every change so reviews have a trail.</p><p>Canary verification is a fixed step before launch. Route 1% of traffic, watch the rejection and error rates, and only scale up once there is no false positive problem. Rate limit config changes go through the same process, because they affect production just like code.</p><h3><strong>5.4 Choosing by scenario</strong></h3><p>The table gives defaults. Before production, weigh the team&#8217;s operational capacity, Redis availability, and how much false positive rate the business tolerates.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_THL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fb7b49e-cf89-4d3f-bb29-93b569d612e5_1666x512.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_THL!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fb7b49e-cf89-4d3f-bb29-93b569d612e5_1666x512.png 424w, https://substackcdn.com/image/fetch/$s_!_THL!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fb7b49e-cf89-4d3f-bb29-93b569d612e5_1666x512.png 848w, https://substackcdn.com/image/fetch/$s_!_THL!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fb7b49e-cf89-4d3f-bb29-93b569d612e5_1666x512.png 1272w, https://substackcdn.com/image/fetch/$s_!_THL!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fb7b49e-cf89-4d3f-bb29-93b569d612e5_1666x512.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_THL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fb7b49e-cf89-4d3f-bb29-93b569d612e5_1666x512.png" width="1456" height="447" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3fb7b49e-cf89-4d3f-bb29-93b569d612e5_1666x512.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:447,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:75194,&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://seabreeze0828.substack.com/i/211191572?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fb7b49e-cf89-4d3f-bb29-93b569d612e5_1666x512.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_!_THL!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fb7b49e-cf89-4d3f-bb29-93b569d612e5_1666x512.png 424w, https://substackcdn.com/image/fetch/$s_!_THL!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fb7b49e-cf89-4d3f-bb29-93b569d612e5_1666x512.png 848w, https://substackcdn.com/image/fetch/$s_!_THL!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fb7b49e-cf89-4d3f-bb29-93b569d612e5_1666x512.png 1272w, https://substackcdn.com/image/fetch/$s_!_THL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3fb7b49e-cf89-4d3f-bb29-93b569d612e5_1666x512.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Start with the goal. Anti-abuse needs accuracy, so use a window. Protecting a database needs a constant rate, so use a leaky bucket. A public API needs both an average rate cap and burst tolerance, so use a token bucket. Once the goal is clear, the design mostly follows.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!MNA3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F57a58e09-e808-4ac2-8186-2082a2de03c2_1779x328.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!MNA3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F57a58e09-e808-4ac2-8186-2082a2de03c2_1779x328.png 424w, https://substackcdn.com/image/fetch/$s_!MNA3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F57a58e09-e808-4ac2-8186-2082a2de03c2_1779x328.png 848w, https://substackcdn.com/image/fetch/$s_!MNA3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F57a58e09-e808-4ac2-8186-2082a2de03c2_1779x328.png 1272w, https://substackcdn.com/image/fetch/$s_!MNA3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F57a58e09-e808-4ac2-8186-2082a2de03c2_1779x328.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!MNA3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F57a58e09-e808-4ac2-8186-2082a2de03c2_1779x328.png" width="1456" height="268" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/57a58e09-e808-4ac2-8186-2082a2de03c2_1779x328.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:268,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:75072,&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://seabreeze0828.substack.com/i/211191572?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F57a58e09-e808-4ac2-8186-2082a2de03c2_1779x328.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_!MNA3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F57a58e09-e808-4ac2-8186-2082a2de03c2_1779x328.png 424w, https://substackcdn.com/image/fetch/$s_!MNA3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F57a58e09-e808-4ac2-8186-2082a2de03c2_1779x328.png 848w, https://substackcdn.com/image/fetch/$s_!MNA3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F57a58e09-e808-4ac2-8186-2082a2de03c2_1779x328.png 1272w, https://substackcdn.com/image/fetch/$s_!MNA3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F57a58e09-e808-4ac2-8186-2082a2de03c2_1779x328.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h2><strong>Chapter 6: Summary and next steps</strong></h2><h3><strong>6.1 The key points</strong></h3><p>Here are the key points.</p><ul><li><p>The fixed window is simple but spikes at boundaries. The sliding window fixes the accuracy with time slices.</p></li><li><p>The leaky bucket guarantees smooth output. The token bucket allows bursts, and it is the default choice in production.</p></li><li><p>Distributed rate limiting puts the decision and the deduction in one Redis Lua script, which is what makes it atomic.</p></li><li><p>The gateway handles coarse limiting, services handle precise limiting, and local limiting covers fallbacks. All three layers together make a complete design.</p></li><li><p>Redis failures need a degradation switch, with fail-open or fail-closed chosen per business.</p></li></ul><p>Memorizing those five only helps in interviews. To actually learn this, run the code in Chapter 3 and then load test it against the pitfalls in Chapter 5.</p><h3><strong>6.2 How rate limiting, circuit breaking, degradation, and isolation fit together</strong></h3><p>These four components get lumped together a lot, but their jobs are distinct.</p><ul><li><p>Rate limiting caps the incoming rate at the entry, so overloaded requests never reach the business layer.</p></li><li><p>Circuit breaking watches the downstream and fails fast after repeated failures, stopping the spread.</p></li><li><p>Degradation decides what to return, fallback data or an error.</p></li><li><p>Isolation confines slow dependencies inside their own thread pool or semaphore, so one endpoint cannot take down the whole service.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!y0II!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71b50ac1-847f-4e6a-b60e-0cb4eae1a091_2165x314.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!y0II!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71b50ac1-847f-4e6a-b60e-0cb4eae1a091_2165x314.png 424w, https://substackcdn.com/image/fetch/$s_!y0II!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71b50ac1-847f-4e6a-b60e-0cb4eae1a091_2165x314.png 848w, https://substackcdn.com/image/fetch/$s_!y0II!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71b50ac1-847f-4e6a-b60e-0cb4eae1a091_2165x314.png 1272w, https://substackcdn.com/image/fetch/$s_!y0II!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71b50ac1-847f-4e6a-b60e-0cb4eae1a091_2165x314.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!y0II!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71b50ac1-847f-4e6a-b60e-0cb4eae1a091_2165x314.png" width="1456" height="211" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/71b50ac1-847f-4e6a-b60e-0cb4eae1a091_2165x314.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:211,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:45442,&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://seabreeze0828.substack.com/i/211191572?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71b50ac1-847f-4e6a-b60e-0cb4eae1a091_2165x314.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_!y0II!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71b50ac1-847f-4e6a-b60e-0cb4eae1a091_2165x314.png 424w, https://substackcdn.com/image/fetch/$s_!y0II!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71b50ac1-847f-4e6a-b60e-0cb4eae1a091_2165x314.png 848w, https://substackcdn.com/image/fetch/$s_!y0II!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71b50ac1-847f-4e6a-b60e-0cb4eae1a091_2165x314.png 1272w, https://substackcdn.com/image/fetch/$s_!y0II!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F71b50ac1-847f-4e6a-b60e-0cb4eae1a091_2165x314.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>The four pieces chain together in order. Rate limiting controls the input rate. Circuit breaking controls downstream failures. Isolation keeps slow dependencies inside their own pool. Degradation gives users a fallback result when things fail. Get the order wrong and both alerting and debugging get confusing.</p><p>A complete traffic management setup looks like this. Gateway limiting blocks most traffic. Service layer limiting protects the primary endpoints. Circuit breaking and degradation cover dependency failures. Isolation controls the blast radius. Those four components plus monitoring make the whole picture.</p><h3><strong>6.3 Questions to keep thinking about</strong></h3><p>Two questions to keep in mind, useful for interviews and for everyday design.</p><blockquote><p>Question Should thresholds be dynamic or fixed? What should drive the adjustment?</p><p>Question Adaptive rate limiting can adjust thresholds from RT or error rates. What problem does that solve, and what new risks does it introduce?</p></blockquote><p>I am not giving answers here. Being able to explain your own trade-offs beats memorizing ten conclusions.</p><h2><strong>References</strong></h2><ul><li><p><a href="https://redis.io/docs/latest/commands/eval/">Redis EVAL documentation</a></p></li><li><p><a href="https://github.com/redisson/redisson/wiki/10.-additional-features#104-rate-limiter">Redisson rate limiter wiki</a></p></li><li><p><a href="https://docs.spring.io/spring-cloud-gateway/docs/3.1.8/reference/html/#the-requestratelimiter-gatewayfilter-factory">Spring Cloud Gateway rate limiter filter documentation</a></p></li><li><p><a href="https://github.com/redis/jedis">Jedis project</a></p></li><li><p><a href="https://stripe.com/blog/rate-limiters">Stripe engineering blog, Scaling your API with rate limiters</a></p></li><li><p><a href="https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api">GitHub REST API rate limits documentation</a></p></li><li><p><a href="https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-request-throttling.html">AWS API Gateway throttling documentation</a></p></li><li><p><a href="https://sre.google/workbook/managing-load/">Google SRE workbook, Managing Load</a></p></li></ul><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://seabreeze0828.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">The Java Knowledge Ocean is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[15 Essential Rules for Designing High-Performance and Robust APIs]]></title><description><![CDATA[A complete developer guide covering rate limiting, thread isolation, async processing, and transaction optimization in Java.]]></description><link>https://seabreeze0828.substack.com/p/15-essential-rules-for-designing</link><guid isPermaLink="false">https://seabreeze0828.substack.com/p/15-essential-rules-for-designing</guid><dc:creator><![CDATA[Dylan Smith]]></dc:creator><pubDate>Fri, 14 Aug 2026 14:30:37 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!fISk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd69b1b0e-37e4-478f-9099-61414320ae2d_1500x896.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://seabreeze0828.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">The Java Knowledge Ocean is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>When designing interfaces, we need to carefully consider many aspects. For example, the <strong>standardization</strong> of interfaces is like traffic rules, enabling orderly interaction between different systems. <br><strong>Security </strong>is also very important. Just like guardians protecting treasures, it prevents data from being maliciously attacked or leaked. <br>At the same time, <strong>extensibility </strong>cannot be ignored. It can make interfaces like elastic containers continuously expand functions as requirements change. <br><strong>Stability </strong>is also crucial. Just like a stable power supply, it provides a reliable guarantee for the normal operation of the system.</p><p>This article will sort out what key points need to be considered when designing good interfaces.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!fISk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd69b1b0e-37e4-478f-9099-61414320ae2d_1500x896.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!fISk!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd69b1b0e-37e4-478f-9099-61414320ae2d_1500x896.png 424w, https://substackcdn.com/image/fetch/$s_!fISk!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd69b1b0e-37e4-478f-9099-61414320ae2d_1500x896.png 848w, https://substackcdn.com/image/fetch/$s_!fISk!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd69b1b0e-37e4-478f-9099-61414320ae2d_1500x896.png 1272w, https://substackcdn.com/image/fetch/$s_!fISk!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd69b1b0e-37e4-478f-9099-61414320ae2d_1500x896.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!fISk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd69b1b0e-37e4-478f-9099-61414320ae2d_1500x896.png" width="1456" height="870" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d69b1b0e-37e4-478f-9099-61414320ae2d_1500x896.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:870,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!fISk!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd69b1b0e-37e4-478f-9099-61414320ae2d_1500x896.png 424w, https://substackcdn.com/image/fetch/$s_!fISk!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd69b1b0e-37e4-478f-9099-61414320ae2d_1500x896.png 848w, https://substackcdn.com/image/fetch/$s_!fISk!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd69b1b0e-37e4-478f-9099-61414320ae2d_1500x896.png 1272w, https://substackcdn.com/image/fetch/$s_!fISk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd69b1b0e-37e4-478f-9099-61414320ae2d_1500x896.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>1. Interface Parameter Verification</h3><p>The legality verification of input parameters is a prerequisite for a good interface. Through the verification of input parameters, we can filter out many invalid requests and improve the stability of the system.</p><p>We can divide the verification of input parameter legality into: routine verification and business verification.</p><p>The so-called routine verification includes: token verification, required field verification, length verification, type verification, and so on.</p><p>Business verification is the verification in specific business scenarios. For example, for the commodity order placement interface, the order amount must be greater than 0.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;e815b88f-e773-4e97-8319-4ddfcb947124&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">public String placeOrder(@RequestBody Order order) {
    if (order.getOrderAmount() &gt; 0) {
        return &#8220;Order placed successfully.&#8221;;
    } else {
        return &#8220;Order amount must be greater than zero.&#8221;;
    }
}</code></pre></div><h3>2. Key Interface Log Printing</h3><p>Key interfaces must have logs, including &#8220;input parameter logs&#8221;, &#8220;output parameter logs&#8221;, and &#8220;exception logs&#8221;. In this way, once a problem occurs, we can quickly locate the problem by checking the logs.</p><p>Of course, if we feel that there are too many logs printed in this way, we can set log levels and print logs of this level only in specific circumstances.</p><p>If we provide an interface for use by the client, then for each incoming request, we must record the request &#8220;input parameters&#8221;, &#8220;output parameters&#8221;, and the &#8220;unique identification ID&#8221; they give.</p><p>If the client reports a problem, we can query the complete link of this request through this unique ID. This way, it is convenient to troubleshoot and locate problems. Secondly, if it is not a problem with our interface, the logs are also evidence and there is no need to argue anymore.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;d29ea24d-5d9d-444b-b7fc-10873f103313&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">@Getter
class InputData {
    private String id;
    private int param;

    public InputData(int param) {
        this.id = UUID.randomUUID().toString();
        this.param = param;
    }
}

public class InterfaceLoggerExample {

    private static final Logger logger = LoggerFactory.getLogger(InterfaceLoggerExample.class);

    public static void main(String[] args) {
        InputData inputData = new InputData(10);

        try {
            // Log the input parameter with its unique identifier
            logger.info(&#8221;Interface input parameter (ID: {}): {}&#8221;, inputData.getId(), inputData.getParam());

            // Simulate interface processing
            int result = processInput(inputData.getParam());

            // Log the output parameter with the same identifier
            logger.info(&#8221;Interface output parameter (ID: {}): {}&#8221;, inputData.getId(), result);
        } catch (Exception e) {
            // Log error message when an error occurs in the interface
            logger.error(&#8221;An error occurred in the interface (ID: {}): &#8220;, inputData.getId(), e);
        }
    }

    private static int processInput(int input) {
        if (input % 2 == 0) {
            return input * 2;
        } else {
            throw new IllegalArgumentException(&#8221;Input is not an even number&#8221;);
        }
    }
}</code></pre></div><h3>3. Interface Idempotency Design</h3><p>The so-called idempotency means that multiple invocations of a method or interface will not change the business state and can ensure that the result of repeated invocations is consistent with the result of a single invocation.</p><p>In development, our main operations are CURD. Among them, the &#8220;read&#8221; operation and the &#8220;delete&#8221; operation are naturally idempotent. What we care about are the &#8220;create&#8221; operation and the &#8220;update&#8221; operation.</p><p>The &#8220;create operation&#8221; must be non-idempotent because it involves the generation of new data. The &#8220;update operation&#8221; may be idempotent or non-idempotent, depending on the specific business scenario.</p><p>For more detailed content on idempotency design, you can refer to another article of mine: <strong><a href="https://medium.com/gitconnected/interview-what-should-be-done-when-order-creation-fails-but-inventory-deduction-succeeds-63b00a2f94ba">Idempotency Control</a></strong></p><h3>4. Interface Throttling Control</h3><p>Throttling is to better maintain the stability of the system. If we provide an interface for use by a third-party system, then throttling of this interface is very necessary.</p><p>On the one hand, throttling can prevent the interface from being brushed, causing unnecessary pressure on the service layer. On the other hand, it is to prevent the interface from being abused.</p><p>Here is a throttling case implemented by using custom annotations + AOP.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;b8fdc525-ccbe-4117-9b90-d5089df8b2e6&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">/**
 * @Target indicates that this annotation can be applied to methods.
 * @Retention indicates that this annotation is available at runtime.
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimiter {
    /**
     * The default value is 1.
     */
    int value() default 1;
    /**
     * The default value is 1 second.
     */
    int durationInSeconds() default 1;
}

/**
 * Aspect class for rate limiting.
 */
@Aspect
@Component
public class RateLimiterAspect {

    /**
     * A concurrent hash map to store rate limiters for different methods.
     */
    private final ConcurrentHashMap&lt;String, RateLimiter&gt; rateLimiters = new ConcurrentHashMap&lt;&gt;();

    /**
     * Pointcut expression to target methods annotated with @RateLimiter.
     */
    @Pointcut(&#8221;@annotation(RateLimiter)&#8221;)
    public void rateLimiterPointcut(RateLimiter rateLimiterAnnotation) {
    }

    /**
     * Advice method that wraps around the targeted methods for rate limiting.
     */
    @Around(&#8221;rateLimiterPointcut(rateLimiterAnnotation)&#8221;)
    public Object around(ProceedingJoinPoint joinPoint, RateLimiter rateLimiterAnnotation) throws Throwable {
        int permits = rateLimiterAnnotation.value();
        int durationInSeconds = rateLimiterAnnotation.durationInSeconds();

        // Use the method signature as the key for the rate limiter.
        String key = joinPoint.getSignature().toLongString();
        com.google.common.util.concurrent.RateLimiter rateLimiter = rateLimiters.computeIfAbsent(key, k -&gt; com.google.common.util.concurrent.RateLimiter.create((double) permits / durationInSeconds));

        // Try to acquire a token. If successful, execute the method. Otherwise, throw an exception.
        if (rateLimiter.tryAcquire()) {
            return joinPoint.proceed();
        } else {
            throw new RuntimeException(&#8221;Rate limit exceeded.&#8221;);
        }
    }
}

/**
 * REST controller class.
 */
@RestController
public class ApiController {

    /**
     * Endpoint with rate limiting set to 10 requests per minute.
     */
    @GetMapping(&#8221;/api/limited&#8221;)
    @RateLimiter(value = 10, durationInSeconds = 60)
    public String limitedEndpoint() {
        return &#8220;This API has a rate limit of 10 requests per minute.&#8221;;
    }

    /**
     * Endpoint without rate limiting.
     */
    @GetMapping(&#8221;/api/unlimited&#8221;)
    public String unlimitedEndpoint() {
        return &#8220;This API has no rate limit.&#8221;;
    }
} </code></pre></div><h3>5. Sensitive Data Masking</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!o2Cn!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd314ba8b-b650-40b9-b226-4cc81e0d9b52_1500x1000.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!o2Cn!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd314ba8b-b650-40b9-b226-4cc81e0d9b52_1500x1000.jpeg 424w, https://substackcdn.com/image/fetch/$s_!o2Cn!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd314ba8b-b650-40b9-b226-4cc81e0d9b52_1500x1000.jpeg 848w, https://substackcdn.com/image/fetch/$s_!o2Cn!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd314ba8b-b650-40b9-b226-4cc81e0d9b52_1500x1000.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!o2Cn!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd314ba8b-b650-40b9-b226-4cc81e0d9b52_1500x1000.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!o2Cn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd314ba8b-b650-40b9-b226-4cc81e0d9b52_1500x1000.jpeg" width="1456" height="971" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d314ba8b-b650-40b9-b226-4cc81e0d9b52_1500x1000.jpeg&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;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!o2Cn!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd314ba8b-b650-40b9-b226-4cc81e0d9b52_1500x1000.jpeg 424w, https://substackcdn.com/image/fetch/$s_!o2Cn!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd314ba8b-b650-40b9-b226-4cc81e0d9b52_1500x1000.jpeg 848w, https://substackcdn.com/image/fetch/$s_!o2Cn!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd314ba8b-b650-40b9-b226-4cc81e0d9b52_1500x1000.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!o2Cn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd314ba8b-b650-40b9-b226-4cc81e0d9b52_1500x1000.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Photo by <a href="https://unsplash.com/@__sobota__?utm_source=medium&amp;utm_medium=referral">Mateusz Soboci&#324;ski</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure></div><p>During the process of interface invocation, some sensitive fields may be involved, such as &#8220;ID number&#8221;, &#8220;bank card number&#8221;, &#8220;address&#8221;, &#8220;mobile phone number&#8221;, etc. These data usually need to be masked. For example, 123&#8211;456&#8211;7890 can be masked as 123&#8211;456-XXX.</p><h3>6. Prerequisite for Requesting an Interface&#8202;&#8212;&#8202;Token</h3><p>The general process is as follows. First, after the user logs in successfully, a token is generated and returned to the front end. At the same time, the back end uses this token as the key and stores the user information as the value in the Redis cache.</p><p>Then, when accessing other interfaces later, bring this token in the request header to request the interface. The back end intercepts this interface through an interceptor and checks whether the key exists in Redis. If it does not exist, it directly returns, indicating that the user is not logged in!</p><p>Of course, some interfaces can be accessed without logging in, such as the registration interface. Then the interceptor can filter these interfaces.</p><h3>7. Considering Exceptions, Timeouts, and Retries When Calling Third-Party Interfaces</h3><p>Whether we provide our interface to a third party or request a third-party interface, we need to consider more. When calling a third-party interface, the first things to consider are timeouts, exceptions, and retries.</p><ul><li><p><em><strong>Timeout</strong></em>: We cannot determine how long a third-party interface will take to return, so we should set a timeout period to ensure that our interface is not stuck.</p></li><li><p><em><strong>Exception</strong></em>: All interfaces may have exceptions. If an exception occurs when calling a third-party interface, there must be logs. At the same time, consider whether to retry or handle it with an alarm.</p></li><li><p><em><strong>Retry</strong></em>: If calling a third-party interface fails, it may be due to network reasons. Do we need to retry? How many times to retry needs to be considered.</p></li></ul><h3>8. Unified Response Data Format</h3><p>In development, when we define an interface, we need to return a response body in a unified format. The response data generally contains three attributes: &#8220;status code&#8221; (code), &#8220;information description&#8221; (message), and &#8220;response data&#8221; (data).</p><p>The client can quickly determine whether the interface request is successful based on the status code. If it is successful, it starts processing the data. If it fails, it directly throws out the message information.</p><h3>9. Single Responsibility of Interface</h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!UBcA!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e1ad17c-2c80-44b7-b28a-0946a69cc5eb_1500x996.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!UBcA!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e1ad17c-2c80-44b7-b28a-0946a69cc5eb_1500x996.jpeg 424w, https://substackcdn.com/image/fetch/$s_!UBcA!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e1ad17c-2c80-44b7-b28a-0946a69cc5eb_1500x996.jpeg 848w, https://substackcdn.com/image/fetch/$s_!UBcA!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e1ad17c-2c80-44b7-b28a-0946a69cc5eb_1500x996.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!UBcA!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e1ad17c-2c80-44b7-b28a-0946a69cc5eb_1500x996.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!UBcA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e1ad17c-2c80-44b7-b28a-0946a69cc5eb_1500x996.jpeg" width="1456" height="967" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6e1ad17c-2c80-44b7-b28a-0946a69cc5eb_1500x996.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:967,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!UBcA!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e1ad17c-2c80-44b7-b28a-0946a69cc5eb_1500x996.jpeg 424w, https://substackcdn.com/image/fetch/$s_!UBcA!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e1ad17c-2c80-44b7-b28a-0946a69cc5eb_1500x996.jpeg 848w, https://substackcdn.com/image/fetch/$s_!UBcA!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e1ad17c-2c80-44b7-b28a-0946a69cc5eb_1500x996.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!UBcA!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6e1ad17c-2c80-44b7-b28a-0946a69cc5eb_1500x996.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Photo by <a href="https://unsplash.com/@pixelesse?utm_source=medium&amp;utm_medium=referral">Ksenia</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure></div><p>The single responsibility principle states that under the condition of clarifying the responsibility of the interface, try to make the interface single as much as possible, that is, one interface only does one thing, not two or more.</p><p>Doing this has many benefits. First, it can make the code easier to maintain. If an interface only does one thing, when this function needs to be modified, it is easy to find the corresponding interface for modification without affecting other unrelated functions. Second, it is convenient for expansion. If a new function needs to be added, a new interface can be created without interfering with the existing interface. Finally, it can improve the readability of the code. The name and function of the interface are clear at a glance, and other developers can know what this interface is for at a glance.</p><p>For example, in a shopping system, there can be an order interface specifically responsible for order-related operations, a commodity interface specifically handling commodity matters, and a user interface focusing on user management. In this way, the responsibilities of each interface are clear, and the system will be more stable and easy to maintain.</p><p>Many non-senior interface designers always think that the more things an interface does, the more convenient it is when designing interfaces. This is a very serious misunderstanding.</p><h3>10. Does the Interface Need to Adopt Asynchronous Processing</h3><p>For example, if you implement a user registration interface. When a user registers successfully, send an email or text message to notify the user.</p><p>This sending of an email or text message is more suitable for asynchronous processing. After all, a failure of a notification type cannot lead to a registration failure.</p><p>So how do we perform asynchronous operations? We can use a message queue. That is, after a user registers successfully, the producer generates a registration success message. When the consumer pulls the registration success message, it sends a notification.</p><h3>11. Thread pool isolation for core interfaces</h3><p>In business code, many interfaces may use thread pools. In addition to core interfaces, some ordinary interfaces may also use thread pools (to improve processing efficiency). If thread pool isolation is not implemented, if an ordinary interface has a bug and fills up the thread pool, it will cause your main business to be affected.</p><h3>12. Improve the response time of interfaces</h3><ol><li><p>Try to use indexes for database queries to optimize query speed.</p></li><li><p>Consider whether to add caches: local caches, Redis caches, ES storage, and so on.</p></li></ol><h3>13. Optimize serial processing of interfaces to parallel processing</h3><p>Suppose we want to develop the homepage of a website. We design an interface for querying homepage data. This interface needs to query user information, header information, news information, and so on.</p><p>The simplest way is to call the interfaces serially one by one. If you want to improve performance, then adopt a parallel calling method and query simultaneously instead of blocking.</p><p>You can use CompletableFuture (recommended) or FutureTask (not recommended).</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;4dca3d5d-d4e7-4d2b-a8f0-d381335d40c4&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">Map&lt;Long, List&lt;SubjectLabelBO&gt;&gt; map = new HashMap&lt;&gt;();
List&lt;CompletableFuture&lt;Map&lt;Long, List&lt;SubjectLabelBO&gt;&gt;&gt;&gt; completableFutureList = 
categoryBOList.stream().map(category -&gt;
        CompletableFuture.supplyAsync(() -&gt; getLabelBOList(category), labelThreadPool)
).collect(Collectors.toList());

completableFutureList.forEach(future -&gt; {
    try {
        Map&lt;Long, List&lt;SubjectLabelBO&gt;&gt; resultMap = future.get(); 
        map.putAll(resultMap);
    } catch (Exception e) {
        e.printStackTrace();
    }
});</code></pre></div><h3>14. Control lock granularity of interfaces</h3><p>In high-concurrency scenarios, we will perform locking operations on shared resources to ensure thread safety. However, if the locking granularity is too large, it will affect the interface performance.</p><p>What is locking granularity? For example, you bring a love letter home but don&#8217;t want your parents to find it. Then you secretly return to your room and put it in a lockable drawer instead of locking the door of the room. This reduces the granularity of the lock.</p><p>Code example:</p><ul><li><p>Excessive lock granularity: Lock methods A and B entirely. But in fact, I only want to lock method A. This is excessive lock granularity.</p></li></ul><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;983ad4e6-cafc-4368-b9ba-3c6118bb93ad&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">void test(){
    synchronized (this) {
       B();
       A();
    }
}</code></pre></div><p>Reduce lock granularity: Only lock method A when it is actually needed instead of locking both method A and method B. This reduces the lock granularity and can improve interface performance in high-concurrency scenarios.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;c6ae226a-5cd2-42f8-b5bc-dd6f6823580e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">void test(){
    B();
    synchronized (this) {
       A();
    }
}</code></pre></div><h3>15. Avoid long transactions</h3><p>During a long transaction, CPU and memory usage may increase. In severe cases, it will lead to slow overall response of the server and make online applications unusable.</p><p>In addition to possible problems with SQL itself, the causes of long transactions are also closely related to the transaction control logic at the application layer.</p><p>How can we avoid long transaction problems as much as possible&#10067;</p><ol><li><p>Do not put remote interface calls inside transactions.</p></li><li><p>If possible, put some query-related operations outside transactions as much as possible.</p></li><li><p>In concurrent scenarios, try to avoid using the @Transactional annotation to operate transactions. Use orchestrated transactions of TransactionTemplate to flexibly control the scope of transactions.</p></li></ol><p>When using <code>@Transactional</code> to manage transactions originally, it is like this.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;74d09184-50a6-4592-b220-d0b7ae2d3753&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">@Transactional
public int createUser(User user){
    // Save user information
    userDao.save(user);
    passCertDao.updateFlag(user.getPassId());
    // This method is a remote interface call
    sendEmailRpc(user.getEmail());
    return user.getUserId();
}</code></pre></div><p>Use <code>TransactionTemplate </code>for orchestrated transactions.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;42fcbc46-cdac-44a5-b391-98b50c73aa70&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">@Resource
private TransactionTemplate transactionTemplate;

public int createUser(User user){
    transactionTemplate.execute(transactionStatus -&gt; {
      try {
         userDao.save(user);
         passCertDao.updateFlag(user.getPassId());
      } catch (Exception e) {
         // Exception manual setup rollback
         transactionStatus.setRollbackOnly();
      }
      return true;
    });
    // This method is a remote interface call
    sendEmailRpc(user.getEmail());
    return user.getUserId();
}</code></pre></div><div><hr></div><p><strong>Finally, if the article was helpful, please clap &#128079;and follow, thank you! &#9584;(*&#176;&#9661;&#176;*)&#9583;</strong></p><p><strong>I&#8217;m Dylan, looking forward to progressing with you. &#10084;&#65039;</strong></p>]]></content:encoded></item><item><title><![CDATA[System Design: Principles and Practices of Idempotency Control]]></title><description><![CDATA[From unique indexes to generic idempotency tables: 5 proven strategies to prevent double deduction in distributed systems]]></description><link>https://seabreeze0828.substack.com/p/system-design-principles-and-practices</link><guid isPermaLink="false">https://seabreeze0828.substack.com/p/system-design-principles-and-practices</guid><dc:creator><![CDATA[Dylan Smith]]></dc:creator><pubDate>Fri, 14 Aug 2026 14:19:00 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!ZheQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9abea9a3-da11-4616-af8b-7569791a2cbb_1313x876.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ZheQ!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9abea9a3-da11-4616-af8b-7569791a2cbb_1313x876.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ZheQ!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9abea9a3-da11-4616-af8b-7569791a2cbb_1313x876.jpeg 424w, https://substackcdn.com/image/fetch/$s_!ZheQ!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9abea9a3-da11-4616-af8b-7569791a2cbb_1313x876.jpeg 848w, https://substackcdn.com/image/fetch/$s_!ZheQ!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9abea9a3-da11-4616-af8b-7569791a2cbb_1313x876.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!ZheQ!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9abea9a3-da11-4616-af8b-7569791a2cbb_1313x876.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ZheQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9abea9a3-da11-4616-af8b-7569791a2cbb_1313x876.jpeg" width="1313" height="876" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9abea9a3-da11-4616-af8b-7569791a2cbb_1313x876.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:876,&quot;width&quot;:1313,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ZheQ!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9abea9a3-da11-4616-af8b-7569791a2cbb_1313x876.jpeg 424w, https://substackcdn.com/image/fetch/$s_!ZheQ!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9abea9a3-da11-4616-af8b-7569791a2cbb_1313x876.jpeg 848w, https://substackcdn.com/image/fetch/$s_!ZheQ!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9abea9a3-da11-4616-af8b-7569791a2cbb_1313x876.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!ZheQ!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9abea9a3-da11-4616-af8b-7569791a2cbb_1313x876.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://seabreeze0828.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">The Java Knowledge Ocean is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2><strong>Background</strong></h2><p>Imagine a real scenario where when the order system calls the inventory deduction method, an interface timeout occurs and the call fails.</p><p>However, the order system cannot determine whether the inventory has actually been deducted successfully although the previous call returned a failure.</p><p>In this case, if a retry is initiated by default, and if there is no idempotency control for inventory deduction, then the same order may have its inventory deducted twice, ultimately leading to a situation of underselling. Therefore, it is necessary to implement idempotency control for inventory deduction.</p><h2><strong>What is idempotency?</strong></h2><p>Idempotency is a concept in mathematics and computer science.</p><ul><li><p>In mathematics, idempotency can be expressed by the function expression: <code>f(x) = f(f(x))</code>. For example, the function for finding the absolute value is idempotent, that is, <code>abs(x) = abs(abs(x))</code>.</p></li><li><p>In computer science, idempotency means that one and multiple requests for a resource should have the same side effects. In other words, the impact of multiple requests is the same as the impact of a single request execution.</p></li></ul><p>According to the definition on Wikipedia, idempotency (idempotent, idempotence) is a concept in mathematics and computer science. In mathematics, idempotency can be expressed by the function expression: <code>f(x) = f(f(x))</code>. For example, the function for finding the absolute value is idempotent, that is, <code>abs(x) = abs(abs(x))</code>.</p><p>In computer science, the characteristic of an idempotent operation is that the impact of any number of executions is the same as the impact of a single execution. An idempotent function or method refers to a function that can be executed repeatedly with the same parameters and obtain the same result. In our daily programming, read operations are naturally idempotent, and only write operations need idempotency control.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Ijgf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ec1fead-13a5-4719-83d1-b6950f6e4ee6_1313x877.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Ijgf!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ec1fead-13a5-4719-83d1-b6950f6e4ee6_1313x877.jpeg 424w, https://substackcdn.com/image/fetch/$s_!Ijgf!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ec1fead-13a5-4719-83d1-b6950f6e4ee6_1313x877.jpeg 848w, https://substackcdn.com/image/fetch/$s_!Ijgf!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ec1fead-13a5-4719-83d1-b6950f6e4ee6_1313x877.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!Ijgf!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ec1fead-13a5-4719-83d1-b6950f6e4ee6_1313x877.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Ijgf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ec1fead-13a5-4719-83d1-b6950f6e4ee6_1313x877.jpeg" width="1313" height="877" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2ec1fead-13a5-4719-83d1-b6950f6e4ee6_1313x877.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:877,&quot;width&quot;:1313,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!Ijgf!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ec1fead-13a5-4719-83d1-b6950f6e4ee6_1313x877.jpeg 424w, https://substackcdn.com/image/fetch/$s_!Ijgf!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ec1fead-13a5-4719-83d1-b6950f6e4ee6_1313x877.jpeg 848w, https://substackcdn.com/image/fetch/$s_!Ijgf!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ec1fead-13a5-4719-83d1-b6950f6e4ee6_1313x877.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!Ijgf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2ec1fead-13a5-4719-83d1-b6950f6e4ee6_1313x877.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><strong>Scenarios requiring idempotency control</strong></h2><p>The just-mentioned inventory deduction scenario is just one of them. Let&#8217;s take a look at all the scenarios that I have encountered in my work that require idempotency control.</p><h3><strong>1. Interface call retry</strong></h3><p>This may be the timeout retry scenario just mentioned. If the call times out but actually has a successful impact, then a success can be returned directly after the second request. It may also not be due to timeout but due to bugs in other code. Therefore, for relatively important write operations, it is best to add idempotency control.</p><p>For cases where the call times out but is indeed a business failure, repeated execution will usually still fail, and generally there is no need for idempotency control for failures. For example, if it was insufficient inventory before the retry, and the inventory has been replenished during the retry, then it is possible to succeed. In this scenario, it is not recommended to directly use the saved idempotent result. It is better to execute the business logic again. Of course, if there are special requirements from the business side, it is an exception.</p><h3><strong>2. Duplicate consumption by MQ consumer groups</strong></h3><p>For the logic of consuming a message, since messages may be read repeatedly, message consumption needs idempotency control. The way of idempotency control can be selected according to the business scenario and will be introduced later.</p><h3><strong>3. Front-end duplicate submissions</strong></h3><p>For example, in the order placement operation here, if the submit button is clicked quickly, two orders may be created instantaneously. Of course, control may not be necessary here, depending on product requirements. After all, orders can be canceled. However, most form duplicate submissions still need to be controlled.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!oeVH!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F622b80bd-2109-4134-ab94-7c239bd39ca2_1313x872.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!oeVH!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F622b80bd-2109-4134-ab94-7c239bd39ca2_1313x872.jpeg 424w, https://substackcdn.com/image/fetch/$s_!oeVH!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F622b80bd-2109-4134-ab94-7c239bd39ca2_1313x872.jpeg 848w, https://substackcdn.com/image/fetch/$s_!oeVH!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F622b80bd-2109-4134-ab94-7c239bd39ca2_1313x872.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!oeVH!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F622b80bd-2109-4134-ab94-7c239bd39ca2_1313x872.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!oeVH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F622b80bd-2109-4134-ab94-7c239bd39ca2_1313x872.jpeg" width="1313" height="872" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/622b80bd-2109-4134-ab94-7c239bd39ca2_1313x872.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:872,&quot;width&quot;:1313,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!oeVH!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F622b80bd-2109-4134-ab94-7c239bd39ca2_1313x872.jpeg 424w, https://substackcdn.com/image/fetch/$s_!oeVH!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F622b80bd-2109-4134-ab94-7c239bd39ca2_1313x872.jpeg 848w, https://substackcdn.com/image/fetch/$s_!oeVH!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F622b80bd-2109-4134-ab94-7c239bd39ca2_1313x872.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!oeVH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F622b80bd-2109-4134-ab94-7c239bd39ca2_1313x872.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><strong>How is idempotency designed?</strong></h2><p>The core of idempotency is to control the impact of the same request. No matter what scheme is adopted, first of all, a unique ID is needed to identify that this request is unique.</p><h3><strong>1. Global unique ID</strong></h3><p>How do we obtain a global unique ID?</p><p>We can use <code>UUID</code>, but the disadvantages of <code>UUID</code> are relatively obvious. Its string takes up a large space, the generated ID is too random, has poor readability, and there is no incrementality.</p><p>We can also use the <code>Snowflake algorithm</code> to generate a unique ID.</p><blockquote><p><em>The Snowflake algorithm is an algorithm for generating globally unique IDs in a distributed environment. The generated IDs are called </em><code>Snowflake IDs</code><em>. This algorithm was created by Twitter and is used for tweet IDs.</em></p></blockquote><p>A Snowflake ID has 64 bits.</p><ul><li><p>The first bit: In Java, the highest bit of a long is the sign bit, representing positive and negative. Positive numbers are 0 and negative numbers are 1. Generally, the generated IDs are all positive, so it defaults to 0.</p></li><li><p>The next 41 bits are timestamps, representing the number of milliseconds since a selected period.</p></li><li><p>The next 10 bits represent the computer ID to prevent conflicts.</p></li><li><p>The remaining 12 bits represent the sequence number for generating IDs on each machine, allowing multiple Snowflake IDs to be created within the same millisecond.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!hFRl!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0da62ab-8984-4c38-8bea-d141f1141cb8_1313x199.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!hFRl!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0da62ab-8984-4c38-8bea-d141f1141cb8_1313x199.png 424w, https://substackcdn.com/image/fetch/$s_!hFRl!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0da62ab-8984-4c38-8bea-d141f1141cb8_1313x199.png 848w, https://substackcdn.com/image/fetch/$s_!hFRl!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0da62ab-8984-4c38-8bea-d141f1141cb8_1313x199.png 1272w, https://substackcdn.com/image/fetch/$s_!hFRl!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0da62ab-8984-4c38-8bea-d141f1141cb8_1313x199.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!hFRl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0da62ab-8984-4c38-8bea-d141f1141cb8_1313x199.png" width="1313" height="199" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f0da62ab-8984-4c38-8bea-d141f1141cb8_1313x199.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:199,&quot;width&quot;:1313,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!hFRl!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0da62ab-8984-4c38-8bea-d141f1141cb8_1313x199.png 424w, https://substackcdn.com/image/fetch/$s_!hFRl!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0da62ab-8984-4c38-8bea-d141f1141cb8_1313x199.png 848w, https://substackcdn.com/image/fetch/$s_!hFRl!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0da62ab-8984-4c38-8bea-d141f1141cb8_1313x199.png 1272w, https://substackcdn.com/image/fetch/$s_!hFRl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff0da62ab-8984-4c38-8bea-d141f1141cb8_1313x199.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><h2><strong>Several solutions for implementing idempotency</strong></h2><h3><strong>1. select + insert + unique index conflict</strong></h3><p>Taking the scenario of deducting inventory as an example, we have a table that records successful inventory deduction records. There are mainly three fields: order number, inventory ID, and the number of deductions.</p><p>In this scenario, the order number <code>orderSn</code> is our unique ID. When a request comes, first <code>select</code> whether there is a deduction record for this order. Then there are three situations:</p><ul><li><p>If the deduction record already exists, intercept the request and directly return success.</p></li><li><p>If the deduction record does not exist, execute <code>insert</code>. If <code>insert</code> is successful, return success normally.</p></li><li><p>If the deduction record does not exist, execute <code>insert</code>. If <code>insert</code> fails, <code>catch</code> and see if it is a <code>DuplicateKeyException</code>. If so, it means that the interval between multiple retry requests is too short and simultaneously bypasses the data judgment of <code>select</code>.</p></li></ul><p>Pseudo code is as follows:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;f9fd2947-828c-4b34-9f4d-0dea6c7ca3d0&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">public boolean deduct(String orderSn, Long inventoryId, Integer deductCount) {
    // Select a record by order serial number.
    Record record = selectByOrderSn(orderSn);

    if (record!= null) {
        // Duplicate request. Return success.
        return true;
    }

    try {
        // Insert data.
        insert(orderSn, inventoryId, deductCount);
    } catch (DuplicateKeyException e) {
        // Unique key conflict. Duplicate request. Return success.
        return true;
    }

    // Normal processing logic... May return false if there is an error, such as insufficient inventory.

    return true;
}</code></pre></div><p>Generally speaking, the insert and the subsequent normal processing logic need to be included in the same transaction. This is because if the first request successfully inserts the deduction record and then an error may occur later. If the previous insert is not rolled back, all requests after the second one will be directly intercepted and return true.</p><p>In addition, there may be a scenario where the inventory is insufficient and the deduction fails. For such a business failure, suppose the caller requires idempotency as well. That is, if the same parameters are passed next time and it also returns failure, then it also needs to be recorded in the deduction record table. At this time, the record table needs to add a <code>status</code> field and an <code>extInfo</code> to record the specific error situation, and then the error details can be directly returned.</p><h3><strong>2. insert + unique index conflict</strong></h3><p>The difference between this solution and the previous one is that there is no need for the initial <code>select</code> query. The rest of the logic is the same. It is used in cases where the probability of duplicate requests is relatively low.</p><h3><strong>3. Status check + update row lock</strong></h3><p>Many business scenarios have states. After a series of business logics are successfully performed, they will flow to the next state. For example, when placing an order, if a coupon is attached, the status of the coupon will be marked as &#8220;occupied state&#8221;. Generally, there are the following states.</p><p>The SQL for occupying a coupon when placing an order can be written like this (generally, it will also record which order and which sku occupies it):</p><pre><code><span>update coupon_instance set status = 2 where coupon_id = &#8216;12315&#8217; and status = 1;</span></code></pre><p>The pseudo code implementation is as follows:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;38adccfe-37e7-4421-ab76-f78293340bbe&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">void holdCoupon(Request request) {
    String couponId = request.getCouponId();
    int rows = &#8220;update coupon_instance set status = 2 where couponId = #{couponId} and status = 1;&#8221;;
    if (rows == 0) {
        // No processing. Return directly.
        return;
    }
    if (rows &gt; 1) {
        // Abnormal situation. Raise an alarm.
        throw new IllegalStateException();
    }
    // rows == 1 is the normal situation. Process other business logic, such as deducting and verifying inventory, disabling certain coupons after occupying the coupon, etc...
}</code></pre></div><p>When the first request for couponId 12135 arrives, the status of this coupon is &#8220;unused&#8221; and needs to be updated to &#8220;occupied state&#8221;. After the <code>update</code> statement is executed, the number of affected rows returned is 1, and the subsequent process is executed normally.</p><p>When the second request for the same coupon comes, the status of this coupon is already &#8220;occupied state&#8221; and needs to be updated to &#8220;occupied state&#8221;. After the <code>update</code> statement is executed, the number of affected rows returned is 0, and it is directly returned.</p><p>In addition, the verification method here has a disadvantage. If not only this method will modify the status field, for example, after the coupon expires, there is a task that modifies the <code>status</code> to 3 - expired. At this time, because <code>row == 0</code> is obtained after the update, so a success is returned. Generally, occupying coupons is called by the order business. If a coupon is expired and a success is still returned to it, then the order placement will continue, which is bound to cause some problems.</p><p>So in this scenario, it is more appropriate to report an error after <code>row == 0</code>. However, if it is changed to report an error, in fact, this method is not idempotent anymore. It only does duplicate prevention control. Because the first call returns success and the second call throws an exception. Duplicate prevention is mainly to avoid generating duplicate data. Just intercepting duplicate requests is fine. In addition to intercepting processed requests, idempotent design also requires that the same request returns the same result. For the case of repeated consumption of messages, I think an error-reporting implementation can be accepted. First, because there will not be too many duplicate messages. Second, after an error is reported, the message will enter the dead letter queue and can be discarded.</p><p>Of course, not only <code>status</code> can be processed in this way, other fields can also be processed. It&#8217;s just that <code>status</code> is more representative.</p><h3><strong>4. Idempotency control table (recommended)</strong></h3><p>In solutions 1 and 2, generally a separate record table needs to be implemented for idempotency. In fact, in a project, there are usually more than one or two points that need idempotency control. So often it is hoped that the idempotency control operation can be separated from the business. Let&#8217;s take a look at the specific design.</p><p>First, create an idempotency control table.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:&quot;5ad1b8b7-657e-4253-b651-edb50fb245eb&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">CREATE TABLE `idempotent` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT &#8216;Auto-increment primary key&#8217;,
  `biz_id` varchar(128) NOT NULL COMMENT &#8216;External request id, unique identifier, can be order_sn, uuid, etc.&#8217;,
  `biz_type` varchar(32) NOT NULL COMMENT &#8216;Business type&#8217;,
  `request` varchar(1024) NOT NULL COMMENT &#8216;Backup of request content&#8217;,
  `response` varchar(1024) DEFAULT NULL COMMENT &#8216;Backup of response content. If status is success, it represents a backup of normal return results. If it is failure, it represents a backup of abnormal content.&#8217;,
  `status` int(10) NOT NULL COMMENT &#8216;Status. 0 - initialization, 1 - success, 2 - failure&#8217;,
  PRIMARY KEY (`id`),
  UNIQUE KEY `biz_id_type` (`biz_id`,`biz_type`)
) ENGINE=InnoDB COMMENT=&#8217;Idempotency table&#8217;;</code></pre></div><p>To enable multiple businesses to be stored in one table, add a biz_type. In this way, if biz_id is the order number, through a combined unique index, idempotency for multiple scenarios can be saved.</p><p>Taking inventory deduction as an example, the core implementation code is as follows:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;4adea44b-4376-4ed5-a503-395d73997317&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">public Response deduct(Request request) {
    // First, check if it has been processed before. If it has been processed successfully before, return the result directly.
    // request.getOrderSn() is bizId, and &#8220;deduct&#8221; is bizType.
    Idempotent idempotent = idempotentDAO.getIdempotent(request.getOrderSn(), &#8220;deduct&#8221;);
    if (idempotent!= null) {
        return JSON.parseObject(idempotent.getResponse(), new TypeReference&lt;Response&gt;() {});
    }
    idempotentDAO.insert(request.getOrderSn(), &#8220;deduct&#8221;, request);

    // Execute business code...
    Response response = new Response();

    // After the business code is executed successfully, update the record.
    idempotentDAO.setStatus(WorkOrderStatusEnum.SUCCESS.getCode());
    idempotentDAO.setResponse(JSON.toJSONString(response));
    idempotentDAO.update(idempotentDAO);
    return response;
}</code></pre></div><p>Observing the above code, it is not difficult to find that this logic is actually relatively fixed and similar to template code. If each piece of business code that requires idempotency control needs to add such before and after logic, it will lead to excessive duplicate code in the project and affect code readability. Is there any way to optimize it?</p><p>In fact, it can be implemented by using aspects combined with annotations. The principle is to use dynamic proxy to dynamically generate a subclass to control access to the real object. This will not be discussed in detail here.</p><h3><strong>5. Idempotency control table + implemented by other methods</strong></h3><p>The method of using MySQL to implement an idempotency control table has a disadvantage that when the data volume reaches a certain level, it will affect the performance of the actual interface.</p><p>Currently, considering that Redis, HBase, etc. can be used for implementation, but I have not actually operated it. After exploration, I will come back and supplement&#8230;&#128517;</p><div><hr></div><p>Okay, that&#8217;s all for today. If this article is helpful to you, I hope you can give it a thumbs up and follow. This is very important to me.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://seabreeze0828.substack.com/?utm_source=substack&amp;utm_medium=email&amp;utm_content=share&amp;action=share&quot;,&quot;text&quot;:&quot;Share The Java Knowledge Ocean&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://seabreeze0828.substack.com/?utm_source=substack&amp;utm_medium=email&amp;utm_content=share&amp;action=share"><span>Share The Java Knowledge Ocean</span></a></p><p></p>]]></content:encoded></item><item><title><![CDATA[​Real-Time Message Push Solutions Unveiled: From Polling to WebSocket, Which is Your Best Choice?]]></title><description><![CDATA[A comprehensive breakdown of Short Polling, Long Polling, SSE, WebSocket, Netty, and MQTT with Java code examples.]]></description><link>https://seabreeze0828.substack.com/p/real-time-message-push-solutions</link><guid isPermaLink="false">https://seabreeze0828.substack.com/p/real-time-message-push-solutions</guid><dc:creator><![CDATA[Dylan Smith]]></dc:creator><pubDate>Fri, 14 Aug 2026 14:07:57 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!jyfl!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F391ee1bf-4307-429f-8268-5375f1b915da_1500x1000.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div><hr></div><p>Real-time message push is ubiquitous in modern applications, from real-time sales data display on e-commerce websites, to instant message notifications on social platforms, and real-time interactions in games. Real-time message push technology permeates our digital lives.</p><p>Today, let&#8217;s dive deep into the secrets of real-time message push and explore which solution, from polling to MQTT, is your best choice.</p><div><hr></div><h3>&#8203;1. Polling&#8202;&#8212;&#8202;Short Polling</h3><p>Short polling is like calling your friend every few minutes to ask, &#8220;Any news yet?&#8221; Whether they have something or not, you call anyway. The frontend uses a scheduled task (setInterval) to send requests to the backend at regular intervals. If the backend has data, it returns it; otherwise, it returns nothing.</p><h4>Frontend Code Example</h4><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;javascript&quot;,&quot;nodeId&quot;:&quot;1d89f766-a394-473d-9fa4-b94945190e9a&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-javascript">// Execute every 3 seconds
setInterval(function() {
  // Send a request to &#8216;/get-data&#8217;
  fetch(&#8217;/get-data&#8217;)
    .then(response =&gt; response.json()) // Parse the response as JSON
    .then(data =&gt; {
      if (data) {
        console.log(&#8217;New message received&#8217;, data); // If data exists, log it to the console
      }
    });
}, 3000); // Time interval is 3000 milliseconds (3 seconds)</code></pre></div><h4>Backend Code Example</h4><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;f7bf4214-636d-4644-acdb-893222160475&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">@RestController
public class ShortPollingController {
    @GetMapping(&#8221;/get-data&#8221;)
    public ResponseEntity&lt;Map&lt;String, Object&gt;&gt; getData() {
        Map&lt;String, Object&gt; result = new HashMap&lt;&gt;();
        // Simulate data retrieval logic
        boolean hasData = new Random().nextBoolean();
        if (hasData) {
            result.put(&#8221;message&#8221;, &#8220;This is a new message&#8221;);
            return ResponseEntity.ok(result);
        } else {
            return ResponseEntity.noContent().build();
        }
    }
}</code></pre></div><p>The issue with short polling is obvious: the frontend frequently sends requests, but only a few or even none may be valid. This wastes server resources, and with high request volumes, the server faces significant pressure.</p><h3>&#8203;2. Polling&#8202;&#8212;&#8202;Long Polling</h3><p>Long polling is smarter. After the frontend sends a request, the backend doesn&#8217;t respond immediately but holds the request until new data is available. If no data arrives within a timeout, the backend responds, and the frontend sends a new request.</p><h4>Frontend Code Example</h4><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;javascript&quot;,&quot;nodeId&quot;:&quot;6e8ac990-af35-49d7-97df-507bd34377ba&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-javascript">function longPolling() {
  // Send a request to &#8216;/long-polling&#8217;
  fetch(&#8217;/long-polling&#8217;)
    .then(response =&gt; response.json()) // Parse the response as JSON
    .then(data =&gt; {
      console.log(&#8217;Long polling received message&#8217;, data); // Log the received message
      // After receiving the message, initiate the next polling
      longPolling();
    });
}

// Start the long</code></pre></div><h4>Backend Code Example</h4><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;f0fab56b-721a-4e4a-b4ee-2b7b58185cc4&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">@RestController
public class LongPollingController {
    private volatile Map&lt;String, Object&gt; data = new HashMap&lt;&gt;();

    @GetMapping(&#8221;/long-polling&#8221;)
    public ResponseEntity&lt;Map&lt;String, Object&gt;&gt; longPolling() {
        // Simulate waiting for new messages
        synchronized (this) {
            while (data.isEmpty()) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }
        ResponseEntity&lt;Map&lt;String, Object&gt;&gt; response = ResponseEntity.ok(data);
        data = new HashMap&lt;&gt;(); // Clear the data
        return response;
    }

    // Simulate a method to receive new messages
    @PostMapping(&#8221;/send-data&#8221;)
    public ResponseEntity&lt;String&gt; sendData(@RequestBody Map&lt;String, Object&gt; newData) {
        synchronized (this) {
            data = newData;
            this.notifyAll();
        }
        return ResponseEntity.ok(&#8221;Message sent successfully&#8221;);
    }
}</code></pre></div><p>Long polling reduces the number of requests but comes with its own set of issues. If the backend has no new data, the request will time out; under high concurrency, a large number of threads accumulate, putting immense pressure on the server.</p><h3>&#8203;3. SSE Data Push Solution</h3><p>SSE (Server-Sent Events) is a one-way communication protocol based on HTTP. The frontend initiates a request, and the backend establishes a long-lived connection. Once the client receives it, a persistent connection is established, allowing the server to push data to the client in real-time when changes occur. This is ideal for scenarios requiring frequent data updates and low latency, such as online chat, real-time monitoring, and news feeds.</p><h4>Frontend Code Example</h4><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;javascript&quot;,&quot;nodeId&quot;:&quot;04dcab61-d2f3-4f37-866a-4acc0256395e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-javascript">// Create an EventSource connection to &#8216;/sse-connect&#8217;
const eventSource = new EventSource(&#8217;/sse-connect&#8217;);

// Handle incoming messages
eventSource.onmessage = function(event) {
  console.log(&#8217;Message received:&#8217;, event.data);
};

// Handle connection errors
eventSource.onerror = function(error) {
  console.error(&#8217;Connection error:&#8217;, error);
};

// Handle successful connection
eventSource.onopen = function() {
  console.log(&#8217;Connection established&#8217;);
};</code></pre></div><h4>Backend Code Example</h4><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;7e6a9e02-aac6-4e4d-a7db-6c9d4d726f06&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">@RestController
public class SseController {

    private final Map&lt;String, SseEmitter&gt; clients = new ConcurrentHashMap&lt;&gt;();

    @GetMapping(value = &#8220;/sse-connect&#8221;, produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public SseEmitter connect() {
        SseEmitter sseEmitter = new SseEmitter();
        String clientId = UUID.randomUUID().toString();

        // Set timeout
        sseEmitter.timeout(0L);

        // Send initial message
        try {
            sseEmitter.send(SseEmitter.event()
                    .data(&#8221;Connection successful&#8221;)
                    .comment(&#8221;Connection established&#8221;));
        } catch (IOException e) {
            e.printStackTrace();
        }

        // Completion callback
        sseEmitter.onCompletion(() -&gt; {
            System.out.println(&#8221;Client &#8220; + clientId + &#8220; disconnected&#8221;);
            clients.remove(clientId);
        });

        // Timeout callback
        sseEmitter.onTimeout(() -&gt; {
            System.out.println(&#8221;Client &#8220; + clientId + &#8220; connection timed out&#8221;);
            clients.remove(clientId);
        });

        // Error callback
        sseEmitter.onError(throwable -&gt; {
            System.out.println(&#8221;Client &#8220; + clientId + &#8220; connection error&#8221;);
            clients.remove(clientId);
        });

        clients.put(clientId, sseEmitter);
        return sseEmitter;
    }

    // Method to push messages</code></pre></div><p>SSE is a solid choice, but it has its limitations, such as being unidirectional&#8202;&#8212;&#8202;data can only be pushed from the server to the client, not the other way around.</p><h3>&#8203;4. WebSocket Solution</h3><p>WebSocket is a game-changer. It establishes a persistent, bidirectional channel, allowing both the client and server to send messages to each other at any time. This makes it ideal for scenarios requiring real-time, two-way communication, such as online chat or multiplayer gaming.</p><h4>Frontend Code Example</h4><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;javascript&quot;,&quot;nodeId&quot;:&quot;f50422a0-c3f1-417d-bbf9-bb61a25e4fb8&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-javascript">// Create a WebSocket connection to &#8216;ws://localhost:8080/ws-connect&#8217;
const ws = new WebSocket(&#8217;ws://localhost:8080/ws-connect&#8217;);

// Handle connection open event
ws.onopen = function() {
    console.log(&#8217;Connection established&#8217;);
    ws.send(&#8217;Client connected&#8217;);
};

// Handle incoming messages
ws.onmessage = function(event) {
    console.log(&#8217;Message received from server:&#8217;, event.data);
};

// Handle connection errors
ws.onerror = function(error) {
    console.error(&#8217;Connection error:&#8217;, error);
};

// Handle connection close event
ws.onclose = function() {
    console.log(&#8217;Connection closed&#8217;);
};</code></pre></div><h4>Backend Code Example</h4><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;99ba96b1-b95a-46b2-92cb-8daa63fbf8b1&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">@ServerEndpoint(&#8221;/ws-connect&#8221;)
@Component
public class WebSocketServer {

    private static final ConcurrentHashMap&lt;String, Session&gt; clients = new ConcurrentHashMap&lt;&gt;();

    @OnOpen
    public void onOpen(Session session, @PathParam(&#8221;userId&#8221;) String userId) {
        System.out.println(&#8221;Client &#8220; + userId + &#8220; is establishing a connection&#8221;);
        clients.put(userId, session);
    }

    @OnMessage
    public void onMessage(String message, @PathParam(&#8221;userId&#8221;) String userId) {
        System.out.println(&#8221;Client &#8220; + userId + &#8220; sent a message to the server: &#8220; + message);
        // Broadcast the message to all clients
        clients.forEach((id, session) -&gt; {
            try {
                session.getBasicRemote().sendText(&#8221;Client &#8220; + userId + &#8220; says: &#8220; + message);
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
    }

    @OnClose
    public void onClose(@PathParam(&#8221;userId&#8221;) String userId) {
        System.out.println(&#8221;Client &#8220; + userId + &#8220; closed the connection&#8221;);
        clients.remove(userId);
    }

    @OnError
    public void onError(Session session, Throwable throwable) {
        System.out.println(&#8221;An error occurred in the client connection&#8221;);
        throwable.printStackTrace();
    }
}</code></pre></div><p>WebSocket performs well but has its pitfalls, such as a relatively complex connection establishment process and higher resource consumption on the server.</p><h3>&#8203;5. Netty Real-Time Push Solution</h3><p>Netty is a high-performance network application framework. Push solutions implemented with Netty are well-suited for scenarios demanding high concurrency and real-time performance, such as large-scale game servers and financial trading systems.</p><h4>Code Example</h4><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;21679d63-3943-4595-a509-2fe5e2dffa95&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">public class NettyServer {

    public static void main(String[] args) throws InterruptedException {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap bootstrap = new ServerBootstrap()
                    .group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer&lt;SocketChannel&gt;() {
                        @Override
                        protected void initChannel(SocketChannel ch) {
                            ch.pipeline().addLast(new NettyServerHandler());
                        }
                    });
            ChannelFuture future = bootstrap.bind(8080).sync();
            System.out.println(&#8221;Netty server started, listening on port 8080&#8221;);
            future.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

// Netty server handler class
public class NettyServerHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        try {
            String message = (String) msg;
            System.out.println(&#8221;Received message from client: &#8220; + message);
            // Broadcast the message to all clients
            ctx.channel().group().writeAndFlush(&#8221;Server received message: &#8220; + message + &#8220;\n&#8221;);
        } finally {
            ReferenceCountUtil.release(msg);
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }
}</code></pre></div><p>Netty is powerful in performance but has a steep learning curve. To fully leverage its advantages, a deep understanding of its principles and APIs is required.</p><h3>&#8203;6. MQTT for Real-Time Message Push</h3><p>MQTT is a lightweight messaging protocol, ideal for resource-constrained devices and unstable network environments, such as IoT devices.</p><h4>Code Example</h4><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;747e99db-7858-4d94-a3cb-c3a76223c8fa&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">public class MqttExample {

    public static void main(String[] args) {
        // MQTT broker URL
        String brokerUrl = &#8220;tcp://localhost:1883&#8221;;
        // Client ID
        String clientId = &#8220;JavaClient&#8221;;
        MqttClient client;
        try {
            client = new MqttClient(brokerUrl, clientId);
            // Connection options
            MqttConnectOptions options = new MqttConnectOptions();
            options.setCleanSession(true);
            // Connect to the broker
            client.connect(options);
            // Subscribe to a topic
            client.subscribe(&#8221;test/topic&#8221;);
            // Set callback
            client.setCallback(new MqttCallback() {
                @Override
                public void connectionLost(Throwable cause) {
                    System.out.println(&#8221;Connection lost&#8221;);
                }

                @Override
                public void messageArrived(String topic, MqttMessage message) {
                    System.out.println(&#8221;Message received: &#8220; + new String(message.getPayload()));
                }

                @Override
                public void deliveryComplete(IMqttDeliveryToken token) {
                    System.out.println(&#8221;Message delivery complete&#8221;);
                }
            });
            // Publish a message
            MqttMessage msg = new MqttMessage(&#8221;Hello MQTT&#8221;.getBytes());
            msg.setQos(1);
            client.publish(&#8221;test/topic&#8221;, msg);
        } catch (MqttException e) {
            e.printStackTrace();
        }
    }
}</code></pre></div><p>MQTT&#8217;s QoS mechanism ensures message reliability but also introduces some additional complexity and latency.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!jyfl!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F391ee1bf-4307-429f-8268-5375f1b915da_1500x1000.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!jyfl!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F391ee1bf-4307-429f-8268-5375f1b915da_1500x1000.jpeg 424w, https://substackcdn.com/image/fetch/$s_!jyfl!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F391ee1bf-4307-429f-8268-5375f1b915da_1500x1000.jpeg 848w, https://substackcdn.com/image/fetch/$s_!jyfl!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F391ee1bf-4307-429f-8268-5375f1b915da_1500x1000.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!jyfl!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F391ee1bf-4307-429f-8268-5375f1b915da_1500x1000.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!jyfl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F391ee1bf-4307-429f-8268-5375f1b915da_1500x1000.jpeg" width="1456" height="971" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/391ee1bf-4307-429f-8268-5375f1b915da_1500x1000.jpeg&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;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!jyfl!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F391ee1bf-4307-429f-8268-5375f1b915da_1500x1000.jpeg 424w, https://substackcdn.com/image/fetch/$s_!jyfl!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F391ee1bf-4307-429f-8268-5375f1b915da_1500x1000.jpeg 848w, https://substackcdn.com/image/fetch/$s_!jyfl!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F391ee1bf-4307-429f-8268-5375f1b915da_1500x1000.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!jyfl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F391ee1bf-4307-429f-8268-5375f1b915da_1500x1000.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Photo by <a href="https://unsplash.com/@markusspiske?utm_source=medium&amp;utm_medium=referral">Markus Spiske</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure></div><h3>Summary</h3><p>In summary, real-time message push solutions each have their strengths. For simple use cases with low request volumes, polling can suffice; for complex scenarios with high request loads, WebSocket and Netty are excellent choices; and for IoT applications, MQTT is the go-to option. Choose the right solution, and your application will navigate the world of real-time message push with ease!</p><div><hr></div><p><strong>Finally, if the article was helpful, please clap &#128079;and follow, thank you! &#9584;(*&#176;&#9661;&#176;*)&#9583;</strong></p><p><strong>I&#8217;m Dylan, looking forward to progressing with you. &#10084;&#65039;</strong></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://seabreeze0828.substack.com/?utm_source=substack&amp;utm_medium=email&amp;utm_content=share&amp;action=share&quot;,&quot;text&quot;:&quot;Share The Java Knowledge Ocean&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://seabreeze0828.substack.com/?utm_source=substack&amp;utm_medium=email&amp;utm_content=share&amp;action=share"><span>Share The Java Knowledge Ocean</span></a></p><p></p>]]></content:encoded></item><item><title><![CDATA[AOP + Guava RateLimiter: How I Implemented Elegant Rate Limiting with Annotations?]]></title><description><![CDATA[A step-by-step guide to building non-intrusive, annotation-driven QPS control in Spring Boot.]]></description><link>https://seabreeze0828.substack.com/p/aop-guava-ratelimiter-how-i-implemented</link><guid isPermaLink="false">https://seabreeze0828.substack.com/p/aop-guava-ratelimiter-how-i-implemented</guid><dc:creator><![CDATA[Dylan Smith]]></dc:creator><pubDate>Fri, 14 Aug 2026 13:58:50 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!aB-5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fa434ec-f683-406a-9cf2-554330a8f97f_1313x619.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!aB-5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fa434ec-f683-406a-9cf2-554330a8f97f_1313x619.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!aB-5!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fa434ec-f683-406a-9cf2-554330a8f97f_1313x619.png 424w, https://substackcdn.com/image/fetch/$s_!aB-5!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fa434ec-f683-406a-9cf2-554330a8f97f_1313x619.png 848w, https://substackcdn.com/image/fetch/$s_!aB-5!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fa434ec-f683-406a-9cf2-554330a8f97f_1313x619.png 1272w, https://substackcdn.com/image/fetch/$s_!aB-5!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fa434ec-f683-406a-9cf2-554330a8f97f_1313x619.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!aB-5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fa434ec-f683-406a-9cf2-554330a8f97f_1313x619.png" width="1313" height="619" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1fa434ec-f683-406a-9cf2-554330a8f97f_1313x619.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:619,&quot;width&quot;:1313,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!aB-5!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fa434ec-f683-406a-9cf2-554330a8f97f_1313x619.png 424w, https://substackcdn.com/image/fetch/$s_!aB-5!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fa434ec-f683-406a-9cf2-554330a8f97f_1313x619.png 848w, https://substackcdn.com/image/fetch/$s_!aB-5!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fa434ec-f683-406a-9cf2-554330a8f97f_1313x619.png 1272w, https://substackcdn.com/image/fetch/$s_!aB-5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1fa434ec-f683-406a-9cf2-554330a8f97f_1313x619.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://seabreeze0828.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">The Java Knowledge Ocean is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2><strong>Preface</strong></h2><blockquote><p><em>When it comes to AOP (Aspect-Oriented Programming), people often first think: &#8220;Oh, that&#8217;s used for logging, transaction management, or permission verification.&#8221;</em></p><p><em>In fact, AOP&#8217;s capabilities go far beyond that. It can also work wonders for interface self-protection in high-concurrency scenarios.</em></p></blockquote><p>Recently, I encountered a real-world scenario in a project: it&#8217;s a scheduled batch task triggered by <mark data-color="rgb(232, 243, 232)" style="background-color: rgb(232, 243, 232); color: rgb(0, 0, 0);">MQ</mark>. On normal days, everything runs smoothly, but during flash sales or when data volume surges, the backlogged messages in <mark data-color="rgb(232, 243, 232)" style="background-color: rgb(232, 243, 232); color: rgb(0, 0, 0);">MQ</mark> are instantly pushed to consumers.</p><p>Although the consumer service can handle the load, the underlying core business database cannot &#8212; massive concurrent queries immediately max out the CPU (usage spikes to 100%), directly affecting the stability of online real-time services.</p><p>Considering the service is deployed as a single node, introducing Redis for distributed rate limiting would be overkill and add extra operational and maintenance costs. Eventually, I decided to use Spring AOP + Guava RateLimiter + custom annotations to implement a non-intrusive, configurable, and lightweight standalone rate limiting component.</p><h2><strong>I. Why Choose AOP + Annotations?</strong></h2><p>Before introducing the code, let&#8217;s clarify the design philosophy.</p><p>When I first started developing, I also liked to hardcode rate limiting logic directly in the Service or Controller layer, for example:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;5bdc10f1-ae5c-43ce-9b2d-42074146cec5&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">// &#10060; Anti-pattern: Hardcoded, mixed logic, and poor reusability
if (!rateLimiter.tryAcquire()) {
    throw new RuntimeException(&#8221;System is busy&#8221;);
}
doBusiness();</code></pre></div><p>The drawbacks of this approach are obvious:</p><ul><li><p>Mixed logic: Non-business rate limiting judgments are embedded in clear business code.</p></li><li><p>Poor reusability: If ten interfaces need rate limiting, you have to write the same code ten times.</p></li><li><p>Difficult maintenance: If you need to adjust the rate limiting strategy (e.g., upgrade to distributed rate limiting), there will be numerous modification points.</p></li></ul><p>The core of AOP (Aspect-Oriented Programming) is &#8220;decoupling&#8221; and &#8220;reusability&#8221;.</p><p>I encapsulate the rate limiting logic into an independent &#8220;aspect&#8221; and use custom annotations <mark data-color="rgb(232, 243, 232)" style="background-color: rgb(232, 243, 232); color: rgb(0, 0, 0);">as &#8220;switches&#8221;.</mark> Simply add an annotation to the target method, and the rate limiting strategy takes effect immediately. For subsequent maintenance and upgrades, you only need to focus on the aspect logic itself without touching any business code.</p><h2><strong>II. Core Principles of Guava RateLimiter</strong></h2><p>The core library I chose this time is Google Guava&#8217;s <code>RateLimiter</code>, which is implemented based on the Token Bucket algorithm.</p><h3><strong>1. Brief Review of Token Bucket</strong></h3><p>Unlike the rigid &#8220;leaky bucket&#8221; (constant rate outflow), its mechanism is more user-friendly:</p><ul><li><p>Token Production: The system puts tokens into the bucket at a fixed rate.</p></li><li><p>Token Consumption: When a request arrives, it must obtain a token before execution.</p></li><li><p>Key Feature: Supports burst traffic. If there are no requests for a period of time, tokens accumulate in the bucket (until reaching the bucket limit). When a burst of traffic arrives, the accumulated tokens can be consumed immediately without queuing.</p></li></ul><h3><strong>2. Two Core Modes</strong></h3><p>Guava thoughtfully provides two implementations:</p><ul><li><p>SmoothBursty (Smooth Burst): Default mode. Suitable for most scenarios, allowing short-term traffic bursts.</p></li><li><p>SmoothWarmingUp (Smooth Warming Up): Warming-up mode. The token issuance rate is slow initially and gradually increases to the target <mark data-color="rgb(232, 243, 232)" style="background-color: rgb(232, 243, 232); color: rgb(0, 0, 0);">QPS over time</mark>. This is very friendly for resources that need &#8220;warm-up&#8221; (such as database connection pools, cache population), preventing them from being overwhelmed during cold starts.</p></li></ul><h3><strong>3. Standalone Warning &#9888;&#65039;</strong></h3><p>Note: <code>Guava RateLimiter</code> is a standalone rate limiting tool! Tokens are stored in the <mark data-color="rgb(232, 243, 232)" style="background-color: rgb(232, 243, 232); color: rgb(0, 0, 0);">current JVM memory.</mark></p><ul><li><p>If your service is deployed on a single machine, it works perfectly.</p></li><li><p>If you deploy 10 machines, each with QPS=5, the total QPS limit of the entire cluster is 50.</p></li></ul><h3><strong>4. Detailed Explanation of Common APIs</strong></h3><p>Mastering APIs proficiently is the foundation of practical application. Here are the core methods of <code>RateLimiter</code>:</p><p><strong>Core Creation Methods</strong></p><p>Method SignatureDescription<code>create(double permitsPerSecond)</code>Creates a SmoothBursty rate limiter, specifying the number of tokens generated per second (default: permitsPerSecond = QPS = bucket capacity).<code>create(double permitsPerSecond, long warmupPeriod, TimeUnit unit)</code>Creates a SmoothWarmingUp rate limiter, specifying QPS + warm-up time.</p><p><strong>Core Acquisition Methods</strong></p><p>Method SignatureDescription<code>double acquire()</code>Blocking acquisition of 1 token. If no token is available, the thread waits indefinitely until successful acquisition.<code>double acquire(int permits)</code>Blocking acquisition of a specified number of tokens (multiple tokens can be acquired at once).</p><p><code>boolean tryAcquire()</code>Non-blocking acquisition of 1 token. Returns immediately: <code>true</code> for success, <code>false</code> for failure (no waiting).<code>boolean tryAcquire(long timeout, TimeUnit unit)</code>Time-limited waiting for 1 token. Returns <code>true</code> if acquired within the timeout period, otherwise <code>false</code>. This is the most recommended usage&#8212;it avoids infinite thread waiting while providing a certain buffer.</p><h2><strong>III. Code Practice: Building an Enterprise-Grade Rate Limiting Component</strong></h2><p>Next, I will implement a fully functional <code>@RateLimit</code> component that supports QPS configuration, blocking/non-blocking modes, timeout control, and warming-up mode.</p><h3><strong>1. Add Dependencies</strong></h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;523c11f0-3571-451e-9e7d-91e5781d23d2&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">&lt;dependency&gt;
    &lt;groupId&gt;com.google.guava&lt;/groupId&gt;
    &lt;artifactId&gt;guava&lt;/artifactId&gt;
    &lt;version&gt;32.1.3-jre&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
    &lt;artifactId&gt;spring-boot-starter-aop&lt;/artifactId&gt;
&lt;/dependency&gt;</code></pre></div><h3><strong>2. Define the </strong><code>@RateLimit</code><strong> Annotation</strong></h3><p>This annotation carries all configuration metadata for rate limiting.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;0db2d15f-f6f1-4531-bc45-d6479ef9a715&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RateLimit {
    /**
     * Rate limiting threshold (QPS), default 5 per second
     */
    double qps() default 5.0;
    /**
     * Token acquisition strategy
     * true: Blocking mode (wait until token is acquired or timeout)
     * false: Non-blocking mode (fail immediately if token is not available)
     */
    boolean block() default true;
    /**
     * Timeout for blocking wait (only effective when block=true)
     * Default 0, indicating infinite wait
     */
    long timeout() default 0;
    /**
     * Timeout unit
     */
    TimeUnit timeUnit() default TimeUnit.MILLISECONDS;
    /**
     * Warm-up time
     * Default 0 (SmoothBursty); set &gt;0 to enable warming-up mode (SmoothWarmingUp)
     */
    long warmupPeriod() default 0;
    /**
     * Warm-up time unit
     */
    TimeUnit warmupUnit() default TimeUnit.SECONDS;
    /**
     * Rate limiting prompt message
     */
    String message() default &#8220;System is busy, please try again later&#8221;;
}</code></pre></div><h3><strong>3. Define Global Exception </strong><code>RateLimitException</code></h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;d920ab6f-7c40-45eb-b587-094fecb734f2&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">public class RateLimitException extends RuntimeException {
    public RateLimitException(String message) {
        super(message);
    }
}</code></pre></div><h3><strong>4. Implement the Aspect </strong><code>RateLimitAop</code></h3><p>This is the &#8220;brain&#8221; of the rate limiting component. Special attention should be paid to instance caching, thread safety, and the execution logic of different strategies.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;9554362e-9b54-4d46-a5f1-1b7983a13aef&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">import com.google.common.util.concurrent.RateLimiter;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;


import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Slf4j
@Aspect
@Component
public class RateLimitAop {
    
    // Use ConcurrentHashMap to cache RateLimiter instances for thread safety
    // Key: Method signature (ClassName.MethodName(ParamType1,ParamType2)), Value: Rate limiter instance
    private final Map&lt;String, RateLimiter&gt; rateLimiterCache = new ConcurrentHashMap&lt;&gt;();
    @Pointcut(&#8221;@annotation(com.example.annotation.RateLimit)&#8221;)
    public void rateLimitPointcut() {}
    @Around(&#8221;rateLimitPointcut()&#8221;)
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        RateLimit annotation = method.getAnnotation(RateLimit.class);
        // 1. Build a unique method key to prevent method overloading conflicts
        String methodKey = buildMethodKey(method);
        
        // 2. Thread-safely create or get the rate limiter
        RateLimiter rateLimiter = rateLimiterCache.computeIfAbsent(methodKey, key -&gt; createRateLimiter(annotation));
        // 3. Execute token acquisition logic
        boolean acquireSuccess;
        if (annotation.block()) {
            // --- Blocking mode ---
            if (annotation.timeout() &lt;= 0) {
                // Infinite wait until success
                rateLimiter.acquire();
                acquireSuccess = true;
            } else {
                // Time-limited wait
                acquireSuccess = rateLimiter.tryAcquire(annotation.timeout(), annotation.timeUnit());
            }
        } else {
            // --- Non-blocking mode ---
            // Try immediately, return on failure
            acquireSuccess = rateLimiter.tryAcquire();
        }
        // 4. Rate limiting interception
        if (!acquireSuccess) {
            log.warn(&#8221;[Rate Limiting Alert] Method {} has too high request frequency and is rejected.&#8221;, methodKey);
            throw new RateLimitException(annotation.message());
        }
        // 5. Release
        return joinPoint.proceed();
    }
    /**
     * Generate method signature: Package.Class.Method(ParamType1,ParamType2)
     */
    private String buildMethodKey(Method method) {
        StringBuilder keyBuilder = new StringBuilder();
        keyBuilder.append(method.getDeclaringClass().getName())
                .append(&#8221;.&#8221;).append(method.getName()).append(&#8221;(&#8221;);
        Class&lt;?&gt;[] parameterTypes = method.getParameterTypes();
        for (int i = 0; i &lt; parameterTypes.length; i++) {
            keyBuilder.append(parameterTypes[i].getSimpleName());
            if (i &lt; parameterTypes.length - 1) {
                keyBuilder.append(&#8221;,&#8221;);
            }
        }
        keyBuilder.append(&#8221;)&#8221;);
        return keyBuilder.toString();
    }
    /**
     * Factory method: Create a specific RateLimiter based on configuration
     */
    private RateLimiter createRateLimiter(RateLimit annotation) {
        if (annotation.warmupPeriod() &gt; 0) {
            log.info(&#8221;Creating warming-up rate limiter: QPS={}, Warmup={}s&#8221;, annotation.qps(), annotation.warmupPeriod());
            return RateLimiter.create(annotation.qps(), annotation.warmupPeriod(), annotation.warmupUnit());
        } else {
            log.info(&#8221;Creating standard rate limiter: QPS={}&#8221;, annotation.qps());
            return RateLimiter.create(annotation.qps());
        }
    }
}</code></pre></div><h3><strong>5. Business Integration Example</strong></h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;01415ed5-0a1c-49f4-896b-a0c34794fffc&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">@Service
public class DataSyncService {

    // Scenario 1: Core data synchronization, allow queuing for 500ms to ensure execution as much as possible
    @RateLimit(qps = 10.0, block = true, timeout = 500)
    public void syncImportantData(List&lt;Data&gt; dataList) {
        // ... Business logic ...
    }
    // Scenario 2: Non-core interface, directly discard requests when traffic is high to protect the system
    @RateLimit(qps = 50.0, block = false, message = &#8220;Too many visitors currently&#8221;)
    public void refreshCache() {
        // ... Refresh logic ...
    }
}</code></pre></div><h2><strong>IV. Advanced: Talking About the &#8220;Well-Known&#8221; Pitfall of Dynamic Proxies</strong></h2><p>When using AOP, there&#8217;s a classic interview question scenario: AOP failure caused by internal method calls within a class. As developers, we should not only know the phenomenon but also understand the reason.</p><h3><strong>Scenario Reproduction</strong></h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;bf8043c7-1ede-466e-a7d2-2c087610301f&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">@Service
public class TradeService {
    public void process() {
        // ... Preprocessing ...
        pay(); // &#10060; Key point here: Directly call internal method
    }


    @RateLimit(qps = 5.0) 
    public void pay() { ... }
}</code></pre></div><h3><strong>Why Does It Fail?</strong></h3><p>Spring AOP is implemented based on dynamic proxies.</p><ul><li><p>When the container starts, Spring generates a proxy object for <code>TradeService</code>.</p></li><li><p>When <code>process()</code> is called externally, it goes through the proxy first.</p></li><li><p>However, when <code>pay()</code> is executed inside <code>process()</code>, <code>this.pay()</code> is used. Here, <code>this</code> refers to the target object itself, not the proxy object.</p></li><li><p>Since it doesn&#8217;t go through the proxy, the aspect logic is naturally bypassed.</p></li></ul><h3><strong>Pitfall Avoidance Suggestions</strong></h3><p>For such issues, I recommend the following solutions:</p><h3><strong>Recommended: Split Method (Best Practice)</strong></h3><p>Split the <code>pay()</code> method into another independent Bean (e.g., <code>PayService</code>). Call it through injection, which naturally complies with the &#8220;call through proxy&#8221; rule and makes the code structure clearer.</p><h3><strong>Recommended: AopContext</strong></h3><p>Directly retrieve the current proxy object from the Spring context. (For modifying old functions)</p><ul><li><p>Enable configuration on the SpringBoot startup class: <code>@EnableAspectJAutoProxy(exposeProxy = true)</code></p></li><li><p>Modify in specific code: <code>((TradeService) AopContext.currentProxy()).pay();</code></p></li></ul><h3><strong>Not Recommended: @Autowired Inject Self</strong></h3><p>Although it can solve the problem, it is prone to circular dependency exceptions and increases system startup risks.</p><h2><strong>V. Advanced Thinking: From Standalone to Distributed</strong></h2><p>Earlier, I emphasized that <code>Guava RateLimiter</code> is for standalone rate limiting. So, if the system scales up to 50 nodes and you need to implement global rate limiting of 1000 requests per second for a downstream API, what should you do?</p><p>At this time, the power of the AOP + annotation design pattern is reflected.</p><p>You don&#8217;t need to modify any business code or delete the <code>@RateLimit</code> annotation at all.</p><p>You only need to do one thing: Modify the implementation of the <code>RateLimitAop</code> aspect.</p><p>Replace the token acquisition logic in the aspect from <code>Guava RateLimiter</code> to Redis + Lua scripts, or directly integrate Redisson&#8217;s <code>RRateLimiter</code>.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;38ac314e-ad8d-4375-8fb3-06c758135538&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">// Pseudocode example: Seamless switch to distributed rate limiting
private RRateLimiter getRedisLimiter(String key) {
    RRateLimiter limiter = redissonClient.getRateLimiter(key);
    // ... Initialize Redis rate limiter ...
    return limiter;
}

// In the around method, replace RateLimiter.tryAcquire() with Redisson&#8217;s implementation
RRateLimiter limiter = getRedisLimiter(methodKey);
if (!limiter.tryAcquire(annotation.qps(), annotation.timeout(), annotation.timeUnit())) {
    throw new RateLimitException(&#8221;Distributed rate limiting is in effect...&#8221;);
}</code></pre></div><p>This is the art of architectural design. The business side is unaware, and the underlying capabilities are upgraded smoothly.</p><h2><strong>VI. Summary and Conclusion</strong></h2><p>Overall, AOP seamlessly integrates infrastructure like rate limiting into business logic &#8212; this is the charm of an elegant architecture: converging complexity at one point to achieve simplicity elsewhere.</p><p>Finally, a frequently adapted famous quote feels particularly fitting here: &#8220;Let architecture handle architecture, and business handle business.&#8221;</p><p>May your code world be well-organized and free of bugs.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://seabreeze0828.substack.com/?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share The Java Knowledge Ocean&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://seabreeze0828.substack.com/?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share The Java Knowledge Ocean</span></a></p><p></p>]]></content:encoded></item><item><title><![CDATA[How to Design a Notification System for One Billion Users? War Stories from the Trenches]]></title><description><![CDATA[A classic system design interview question, plus real-world engineering lessons learned. Starting from a dumb cron job, scaling up to a billion users &#8212; with architecture diagrams]]></description><link>https://seabreeze0828.substack.com/p/how-to-design-a-notification-system</link><guid isPermaLink="false">https://seabreeze0828.substack.com/p/how-to-design-a-notification-system</guid><dc:creator><![CDATA[Dylan Smith]]></dc:creator><pubDate>Mon, 10 Aug 2026 03:59:39 GMT</pubDate><enclosure url="https://images.unsplash.com/photo-1600960568458-7966d439289e?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw1fHxub3RpZmljYXRpb258ZW58MHx8fHwxNzg2MjI3NDk3fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://images.unsplash.com/photo-1600960568458-7966d439289e?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw1fHxub3RpZmljYXRpb258ZW58MHx8fHwxNzg2MjI3NDk3fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://images.unsplash.com/photo-1600960568458-7966d439289e?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw1fHxub3RpZmljYXRpb258ZW58MHx8fHwxNzg2MjI3NDk3fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 424w, https://images.unsplash.com/photo-1600960568458-7966d439289e?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw1fHxub3RpZmljYXRpb258ZW58MHx8fHwxNzg2MjI3NDk3fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 848w, https://images.unsplash.com/photo-1600960568458-7966d439289e?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw1fHxub3RpZmljYXRpb258ZW58MHx8fHwxNzg2MjI3NDk3fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1272w, https://images.unsplash.com/photo-1600960568458-7966d439289e?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw1fHxub3RpZmljYXRpb258ZW58MHx8fHwxNzg2MjI3NDk3fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1456w" sizes="100vw"><img src="https://images.unsplash.com/photo-1600960568458-7966d439289e?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw1fHxub3RpZmljYXRpb258ZW58MHx8fHwxNzg2MjI3NDk3fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080" width="5184" height="3888" data-attrs="{&quot;src&quot;:&quot;https://images.unsplash.com/photo-1600960568458-7966d439289e?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw1fHxub3RpZmljYXRpb258ZW58MHx8fHwxNzg2MjI3NDk3fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:3888,&quot;width&quot;:5184,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;green and white apple logo&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="green and white apple logo" title="green and white apple logo" srcset="https://images.unsplash.com/photo-1600960568458-7966d439289e?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw1fHxub3RpZmljYXRpb258ZW58MHx8fHwxNzg2MjI3NDk3fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 424w, https://images.unsplash.com/photo-1600960568458-7966d439289e?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw1fHxub3RpZmljYXRpb258ZW58MHx8fHwxNzg2MjI3NDk3fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 848w, https://images.unsplash.com/photo-1600960568458-7966d439289e?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw1fHxub3RpZmljYXRpb258ZW58MHx8fHwxNzg2MjI3NDk3fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1272w, https://images.unsplash.com/photo-1600960568458-7966d439289e?crop=entropy&amp;cs=tinysrgb&amp;fit=max&amp;fm=jpg&amp;ixid=M3wzMDAzMzh8MHwxfHNlYXJjaHw1fHxub3RpZmljYXRpb258ZW58MHx8fHwxNzg2MjI3NDk3fDA&amp;ixlib=rb-4.1.0&amp;q=80&amp;w=1080 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Photo by <a href="https://unsplash.com/@brett_jordan">Brett Jordan</a> on <a href="https://unsplash.com">Unsplash</a></figcaption></figure></div><h2><strong>Let&#8217;s Start with a Quick Rant</strong></h2><p>Ever notice how your phone buzzes nonstop all day? Order shipped, someone replied to your comment, that app you haven&#8217;t opened in three days desperately wants your attention again.</p><p>Sounds simple enough, right? Just send a message. How hard can it be?</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://seabreeze0828.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">Java, SpringBoot &amp; AI Development: Coding Skills &amp; Interview is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>Now try doing it for <strong>one billion users</strong>, sending <strong>billions of notifications per day</strong>, peaking at <strong>hundreds of thousands per second</strong> &#8212; and oh by the way, verification codes absolutely must arrive within three seconds. Still think it&#8217;s easy?</p><p>Today we&#8217;ll start from the most naive approach and build it up layer by layer, talking about what breaks at each stage. Useful for interviews, and even more useful for not repeating my mistakes in production.</p><h2><strong>Version 1: Database + Cron Job (Baby&#8217;s First Notification System)</strong></h2><p>Full transparency &#8212; I actually shipped something like this early in my career. A <code>notifications</code> table with user_id, title, content, status, and a cron job that runs every minute:</p><pre><code><span>Scan table &#8594; Grab pending rows &#8594; Call push API &#8594; Update status</span></code></pre><p>Brutally simple. Ten minutes to code, works great for a small team.</p><h3><strong>Here&#8217;s the Pseudocode</strong></h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;cab3c0a2-9eba-4ec9-a3b4-84309be279fa&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python"># Runs every minute via cron
def cron_send_notifications():
    pending = db.query(&#8221;SELECT * FROM notifications WHERE status = &#8216;pending&#8217; LIMIT 1000&#8221;)
    
    for msg in pending:
        try:
            push_gateway.send(msg.user_id, msg.title, msg.content)
            db.update(msg.id, status=&#8217;sent&#8217;)
        except Exception as e:
            db.update(msg.id, status=&#8217;failed&#8217;, error=str(e))</code></pre></div><h3><strong>And Then Everything Breaks</strong></h3><p><strong>Problem #1: It&#8217;s slow.</strong> As data grows, scanning the table gets worse and worse. Finding 1,000 pending rows among 10 million records &#8212; even with good indexes, doing this every minute destroys your DB.</p><p><strong>Problem #2: Single point of failure.</strong> One cron job on one server. If that box dies, notifications stop entirely. No redundancy, no fallback.</p><p><strong>Problem #3: No rate control.</strong> Marketing dumps a million rows in for a campaign, cron goes brrr, and your push gateway gets DDoS&#8217;d by your own system.</p><p><strong>Problem #4: Terrible latency.</strong> If it runs every minute, a user might wait almost 60 seconds for a verification code. By then they&#8217;ve already hit &#8220;resend&#8221; five times.</p><p><strong>Verdict:</strong> Fine for 10k users. Beyond that? Disaster.</p><h2><strong>Version 2: Add a Message Queue (Now We&#8217;re Cooking)</strong></h2><p>Anyone with a bit of experience will say &#8212; just use Kafka!</p><p>And yeah, that&#8217;s exactly what message queues are for. Producers drop notification tasks in, consumers chew through them at their own pace. Decoupled, buffered, scalable.</p><h3><strong>Architecture Diagram</strong></h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Qaf2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c6c28d8-8b41-42d4-ada8-bcce8a7be608_992x365.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Qaf2!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c6c28d8-8b41-42d4-ada8-bcce8a7be608_992x365.png 424w, https://substackcdn.com/image/fetch/$s_!Qaf2!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c6c28d8-8b41-42d4-ada8-bcce8a7be608_992x365.png 848w, https://substackcdn.com/image/fetch/$s_!Qaf2!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c6c28d8-8b41-42d4-ada8-bcce8a7be608_992x365.png 1272w, https://substackcdn.com/image/fetch/$s_!Qaf2!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c6c28d8-8b41-42d4-ada8-bcce8a7be608_992x365.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Qaf2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c6c28d8-8b41-42d4-ada8-bcce8a7be608_992x365.png" width="992" height="365" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5c6c28d8-8b41-42d4-ada8-bcce8a7be608_992x365.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:365,&quot;width&quot;:992,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!Qaf2!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c6c28d8-8b41-42d4-ada8-bcce8a7be608_992x365.png 424w, https://substackcdn.com/image/fetch/$s_!Qaf2!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c6c28d8-8b41-42d4-ada8-bcce8a7be608_992x365.png 848w, https://substackcdn.com/image/fetch/$s_!Qaf2!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c6c28d8-8b41-42d4-ada8-bcce8a7be608_992x365.png 1272w, https://substackcdn.com/image/fetch/$s_!Qaf2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5c6c28d8-8b41-42d4-ada8-bcce8a7be608_992x365.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3><strong>Consumer Pseudocode</strong></h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;9a759218-f9d7-48e9-ae7f-4448c27a1f0e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">def notification_worker():
    consumer = KafkaConsumer(topic=&#8217;notifications&#8217;, group_id=&#8217;push-group&#8217;)
    
    for message in consumer:
        msg = parse(message.value)
        
        try:
            push_gateway.send(msg.user_id, msg.title, msg.content)
            update_status(msg.id, &#8216;sent&#8217;)
            consumer.commit()  # Commit offset
        except Exception as e:
            update_status(msg.id, &#8216;failed&#8217;, error=str(e))
            # Failures go to retry topic
            retry_topic.produce(msg)</code></pre></div><h3><strong>What This Fixes</strong></h3><p>&#9989; <strong>Decoupling</strong> &#8212; producers don&#8217;t care when things get sent, they just fire and forget<br>&#9989; <strong>Horizontal scaling</strong> &#8212; too slow? Add more workers. Done.<br>&#9989; <strong>Peak shaving</strong> &#8212; traffic spikes buffer in the queue, downstream stays healthy</p><h3><strong>But New Problems Emerge</strong></h3><p><strong>Problem #1: Too many channels.</strong> Push isn&#8217;t just app push. There&#8217;s APNs for iOS, FCM for Android, SMS, email, WeChat templates, in-app messages&#8230; every single one has different APIs, rate limits, retry logic. Throwing them all in one worker pool is chaos.</p><p><strong>Problem #2: No prioritization.</strong> Verification codes and Black Friday marketing blasts share the same queue. When a marketing flood hits, your OTPs get stuck behind for half an hour. That&#8217;s a P0 incident waiting to happen.</p><p><strong>Problem #3: User preferences are nowhere.</strong> Some people hate being woken up at night, some opted out of marketing entirely. We have nowhere to store these rules, nowhere to enforce them.</p><p><strong>Problem #4: Duplicate deliveries.</strong> Kafka has at-least-once delivery semantics. A worker crashes mid-processing, restarts, consumes again &#8212; user gets the same notification twice. Bad UX.</p><h2><strong>Version 3: Layered Architecture (Starting to Look Like a Real System)</strong></h2><p>Okay, let&#8217;s fix each of those problems one by one.</p><h3><strong>Upgraded Architecture Diagram</strong></h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!SM2m!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7643ffd6-6c7d-4047-9eb1-23ca7a43ae77_978x589.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!SM2m!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7643ffd6-6c7d-4047-9eb1-23ca7a43ae77_978x589.png 424w, https://substackcdn.com/image/fetch/$s_!SM2m!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7643ffd6-6c7d-4047-9eb1-23ca7a43ae77_978x589.png 848w, https://substackcdn.com/image/fetch/$s_!SM2m!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7643ffd6-6c7d-4047-9eb1-23ca7a43ae77_978x589.png 1272w, https://substackcdn.com/image/fetch/$s_!SM2m!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7643ffd6-6c7d-4047-9eb1-23ca7a43ae77_978x589.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!SM2m!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7643ffd6-6c7d-4047-9eb1-23ca7a43ae77_978x589.png" width="978" height="589" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7643ffd6-6c7d-4047-9eb1-23ca7a43ae77_978x589.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:589,&quot;width&quot;:978,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!SM2m!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7643ffd6-6c7d-4047-9eb1-23ca7a43ae77_978x589.png 424w, https://substackcdn.com/image/fetch/$s_!SM2m!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7643ffd6-6c7d-4047-9eb1-23ca7a43ae77_978x589.png 848w, https://substackcdn.com/image/fetch/$s_!SM2m!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7643ffd6-6c7d-4047-9eb1-23ca7a43ae77_978x589.png 1272w, https://substackcdn.com/image/fetch/$s_!SM2m!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7643ffd6-6c7d-4047-9eb1-23ca7a43ae77_978x589.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3><strong>Upgrade #1: Priority Queues</strong></h3><p>Straightforward &#8212; split into three topics by importance:</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!tlGI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F796a5926-932d-4568-a3a0-0db41aa0a0dd_958x223.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!tlGI!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F796a5926-932d-4568-a3a0-0db41aa0a0dd_958x223.png 424w, https://substackcdn.com/image/fetch/$s_!tlGI!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F796a5926-932d-4568-a3a0-0db41aa0a0dd_958x223.png 848w, https://substackcdn.com/image/fetch/$s_!tlGI!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F796a5926-932d-4568-a3a0-0db41aa0a0dd_958x223.png 1272w, https://substackcdn.com/image/fetch/$s_!tlGI!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F796a5926-932d-4568-a3a0-0db41aa0a0dd_958x223.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!tlGI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F796a5926-932d-4568-a3a0-0db41aa0a0dd_958x223.png" width="958" height="223" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/796a5926-932d-4568-a3a0-0db41aa0a0dd_958x223.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:223,&quot;width&quot;:958,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!tlGI!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F796a5926-932d-4568-a3a0-0db41aa0a0dd_958x223.png 424w, https://substackcdn.com/image/fetch/$s_!tlGI!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F796a5926-932d-4568-a3a0-0db41aa0a0dd_958x223.png 848w, https://substackcdn.com/image/fetch/$s_!tlGI!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F796a5926-932d-4568-a3a0-0db41aa0a0dd_958x223.png 1272w, https://substackcdn.com/image/fetch/$s_!tlGI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F796a5926-932d-4568-a3a0-0db41aa0a0dd_958x223.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Resource isolation. Low priority traffic can blow up all it wants &#8212; it won&#8217;t touch the high priority stuff. Simple but brutally effective.</p><h3><strong>Upgrade #2: Channel Adapter Pattern</strong></h3><p>Each channel handles its own rate limiting, retries, and auth &#8212; exposing one unified interface.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;513483d1-7f87-4d05-b05e-1c97967cb6bc&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python"># Abstract base class
class ChannelAdapter:
    def send(self, user_id, title, content, extra=None):
        raise NotImplementedError

# Concrete implementation
class APNsAdapter(ChannelAdapter):
    def __init__(self):
        self.rate_limiter = TokenBucket(rate=5000, capacity=10000)
        self.retry_policy = ExponentialBackoff(max_retries=3)
    
    def send(self, user_id, title, content, extra=None):
        if not self.rate_limiter.acquire():
            raise RateLimitExceeded()
        
        token = apns_token_store.get(user_id)
        payload = build_apns_payload(title, content, extra)
        return apns_client.push(token, payload)</code></pre></div><pre><code><span># Factory to get the right channel
channel_map = {
    &#8216;apns&#8217;: APNsAdapter(),
    &#8216;fcm&#8217;: FCMAdapter(),
    &#8216;sms&#8217;: SMSAdapter(),
    &#8216;email&#8217;: EmailAdapter(),
}</span></code></pre><h3><strong>Upgrade #3: User Preference Center</strong></h3><p>Before sending anything, check preferences. If the rules say skip, skip.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;65d803cd-a1ac-4055-83cf-b383f284c5b9&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">def check_user_preferences(user_id, notification):
    prefs = preference_service.get(user_id)
    
    # 1. Is this channel even enabled?
    if notification.channel not in prefs.enabled_channels:
        return False, &#8220;channel disabled&#8221;
    
    # 2. Are we in Do-Not-Disturb hours?
    if prefs.dnd_enabled and is_in_dnd_time(prefs.dnd_start, prefs.dnd_end):
        if notification.priority != &#8216;P0&#8217;:  # High priority bypasses DND
            return False, &#8220;in DND period&#8221;
    
    # 3. Hit the daily cap yet?
    today_count = daily_counter.get(user_id, notification.category)
    if today_count &gt;= prefs.daily_limit.get(notification.category, 5):
        return False, &#8220;daily limit exceeded&#8221;
    
    # 4. Subscribed to this topic?
    if notification.topic and notification.topic not in prefs.subscribed_topics:
        return False, &#8220;topic not subscribed&#8221;
    
    return True, &#8220;ok&#8221;</code></pre></div><h3><strong>Upgrade #4: Idempotency / Deduplication</strong></h3><p>Redis SETNX. Dead simple, works great.</p><pre><code><span>def is_duplicate(notification_id):
    key = f&#8221;notif:sent:{notification_id}&#8221;
    # If SET succeeds, it&#8217;s not a duplicate; 24h TTL
    result = redis.set(key, &#8220;1&#8221;, ex=86400, nx=True)
    return result is None  # None means key existed &#8594; duplicate</span></code></pre><h3><strong>Full Notification Flow Sequence Diagram</strong></h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!PuE_!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec8c70cd-237b-4907-b7fb-e2f72e7789fe_1313x560.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!PuE_!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec8c70cd-237b-4907-b7fb-e2f72e7789fe_1313x560.png 424w, https://substackcdn.com/image/fetch/$s_!PuE_!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec8c70cd-237b-4907-b7fb-e2f72e7789fe_1313x560.png 848w, https://substackcdn.com/image/fetch/$s_!PuE_!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec8c70cd-237b-4907-b7fb-e2f72e7789fe_1313x560.png 1272w, https://substackcdn.com/image/fetch/$s_!PuE_!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec8c70cd-237b-4907-b7fb-e2f72e7789fe_1313x560.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!PuE_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec8c70cd-237b-4907-b7fb-e2f72e7789fe_1313x560.png" width="1313" height="560" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ec8c70cd-237b-4907-b7fb-e2f72e7789fe_1313x560.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:560,&quot;width&quot;:1313,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!PuE_!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec8c70cd-237b-4907-b7fb-e2f72e7789fe_1313x560.png 424w, https://substackcdn.com/image/fetch/$s_!PuE_!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec8c70cd-237b-4907-b7fb-e2f72e7789fe_1313x560.png 848w, https://substackcdn.com/image/fetch/$s_!PuE_!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec8c70cd-237b-4907-b7fb-e2f72e7789fe_1313x560.png 1272w, https://substackcdn.com/image/fetch/$s_!PuE_!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec8c70cd-237b-4907-b7fb-e2f72e7789fe_1313x560.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3><strong>Is This Good Enough?</strong></h3><p>For a mid-sized company with a few million DAU, this architecture will serve you well.</p><p>But for <strong>one billion users</strong>? We&#8217;re not even close.</p><p>Now comes the hard stuff.</p><h2><strong>Version 4: Billion-Scale Distributed Architecture (The Final Form)</strong></h2><p>Let&#8217;s start with the big picture, then break down each piece.</p><h3><strong>Global Architecture Overview</strong></h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!w5JV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4c99473-e2be-49af-8dce-698318de60b0_1313x716.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!w5JV!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4c99473-e2be-49af-8dce-698318de60b0_1313x716.png 424w, https://substackcdn.com/image/fetch/$s_!w5JV!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4c99473-e2be-49af-8dce-698318de60b0_1313x716.png 848w, https://substackcdn.com/image/fetch/$s_!w5JV!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4c99473-e2be-49af-8dce-698318de60b0_1313x716.png 1272w, https://substackcdn.com/image/fetch/$s_!w5JV!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4c99473-e2be-49af-8dce-698318de60b0_1313x716.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!w5JV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4c99473-e2be-49af-8dce-698318de60b0_1313x716.png" width="1313" height="716" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f4c99473-e2be-49af-8dce-698318de60b0_1313x716.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:716,&quot;width&quot;:1313,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!w5JV!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4c99473-e2be-49af-8dce-698318de60b0_1313x716.png 424w, https://substackcdn.com/image/fetch/$s_!w5JV!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4c99473-e2be-49af-8dce-698318de60b0_1313x716.png 848w, https://substackcdn.com/image/fetch/$s_!w5JV!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4c99473-e2be-49af-8dce-698318de60b0_1313x716.png 1272w, https://substackcdn.com/image/fetch/$s_!w5JV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4c99473-e2be-49af-8dce-698318de60b0_1313x716.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><strong>Key Components, One by One</strong></h2><h3><strong>&#9312; Multi-Region Deployment: Proximity Matters</strong></h3><p>A billion users aren&#8217;t all in one country. Host everything in Shanghai, and your US users get hundreds of milliseconds of latency &#8212; if the packets even arrive at all.</p><p>So you deploy a full stack in each major region:</p><ul><li><p>Users route to the nearest region via GeoDNS</p></li><li><p>Each region has its own independent Kafka, workers, channel adapters</p></li><li><p>Global data like user preferences syncs via Cassandra multi-region multi-master replication</p></li></ul><p>Bonus benefit: disaster recovery. If APAC goes down, shift traffic to US East and business keeps running.</p><h3><strong>&#9313; Batch Push Pipeline: Don&#8217;t Flood the Queue with 100M Messages</strong></h3><p>Marketing says &#8220;send a campaign announcement to all users.&#8221; You dump 100 million messages straight into Kafka? Say goodbye to real-time notifications for hours.</p><p>The right approach is a dedicated <strong>batch dispatcher</strong>:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;b13c2e76-544a-443a-b749-111008c3772e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">def batch_push_campaign(campaign_id, segment_id, template_id):
    # 1. Stream users from segmentation system, don&#8217;t load all at once
    user_stream = user_segment_service.export_stream(segment_id)
    
    # 2. Shard by region, distribute to each region&#8217;s queue
    for batch in user_stream.chunks(batch_size=1000):
        region_users = group_by_region(batch)
        
        for region, users in region_users.items():
            kafka = get_kafka_for_region(region)
            # Throttle write rate so we don&#8217;t overwhelm downstream
            rate_limiter.wait()
            kafka.produce_many([
                build_notification_msg(u, template_id, campaign_id)
                for u in users
            ])
    
    # 3. Support pause, cancel, and dynamic rate adjustment
    campaign_tracker.update(campaign_id, status=&#8217;running&#8217;)</code></pre></div><p>The whole trick is <strong>streaming reads + rate-limited writes</strong> &#8212; spread 100 million messages smoothly across several hours without impacting real-time traffic.</p><h3><strong>&#9314; Circuit Breakers + Backpressure: Don&#8217;t Die When Third Parties Die</strong></h3><p>SMS provider goes down. FCM throttles you. This stuff happens all the time. You can&#8217;t just keep retrying and fill up your entire queue.</p><p>Every channel adapter needs a circuit breaker:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;python&quot;,&quot;nodeId&quot;:&quot;31d22f0b-c2a5-41e7-8324-a53e096f9ffe&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-python">class CircuitBreaker:
    CLOSED = &#8216;closed&#8217;       # Normal operation, let requests through
    OPEN = &#8216;open&#8217;           # Failing too much, reject immediately
    HALF_OPEN = &#8216;half_open&#8217; # Testing if things have recovered
    
    def __init__(self, failure_threshold=0.3, recovery_timeout=60):
        self.state = self.CLOSED
        self.failure_count = 0
        self.total_count = 0
        self.failure_threshold = failure_threshold
        self.recovery_timeout = recovery_timeout
    
    def execute(self, func, *args, **kwargs):
        if self.state == self.OPEN:
            if time.time() - self.last_failure_time &gt; self.recovery_timeout:
                self.state = self.HALF_OPEN
            else:
                raise CircuitBreakerOpen()
        
        try:
            result = func(*args, **kwargs)
            self.on_success()
            return result
        except Exception as e:
            self.on_failure()
            # Failed messages go to dead letter queue for later retry
            dead_letter_queue.produce({
                &#8216;func&#8217;: func.__name__, 
                &#8216;args&#8217;: args, 
                &#8216;error&#8217;: str(e)
            })
            raise
    
    def on_success(self):
        self.total_count += 1
        if self.state == self.HALF_OPEN:
            self.state = self.CLOSED  # Probe succeeded, back to normal
    
    def on_failure(self):
        self.failure_count += 1
        self.total_count += 1
        self.last_failure_time = time.time()
        
        if self.total_count &gt;= 100:  # Wait for enough samples
            failure_rate = self.failure_count / self.total_count
            if failure_rate &gt;= self.failure_threshold:
                self.state = self.OPEN</code></pre></div><p>Workers also monitor their own consumer lag. If the backlog gets too big, auto-scale more workers or signal upstream to slow down. That&#8217;s backpressure.</p><h3><strong>&#9315; Full-Link Tracing: Finding One Notification Among Billions</strong></h3><p>Product manager walks over: &#8220;Why didn&#8217;t user X get that notification yesterday?&#8221;</p><p>Without tracing, enjoy your needle-in-haystack log dive.</p><p>The right approach: every notification carries a <code>trace_id</code> from API entry through to delivery receipt, with structured logs at every step:</p><pre><code><span>[API] received trace_id=abc123, user_id=12345
[Orchestrator] deduplicated? No, preferences check? Passed, routed to P1
[Kafka] produced to topic=notifications-p1, offset=45678
[Worker] consumed trace_id=abc123, channel=apns
[Channel] APNs response: success, apns_id=xyz789
[Delivery] receipt received, delivered_at=2024-08-15T10:23:45Z</span></code></pre><p>Ship all logs to ClickHouse, queryable by user ID or trace ID. Billions of rows, sub-second answers.</p><h3><strong>&#9316; Smart Scheduling &amp; Fatigue Management</strong></h3><p>More notifications &#8800; better. Send too many, and users just turn them all off. Then you have nothing.</p><p>So you need:</p><ul><li><p><strong>Frequency capping</strong> &#8212; max N per user per day, per category</p></li><li><p><strong>Time optimization</strong> &#8212; send during each user&#8217;s active hours based on timezone and history</p></li><li><p><strong>Smart throttling</strong> &#8212; if someone ignores several in a row, they&#8217;re probably not interested &#8212; dial it back</p></li><li><p><strong>One-click unsubscribe</strong> &#8212; compliance (GDPR, CAN-SPAM) and basic respect</p></li></ul><h3><strong>&#9317; Template System: Don&#8217;t Hardcode Content</strong></h3><p>Not going deep here, but: titles, bodies, deep links all live as templates with variable substitution, i18n support, and A/B test variants. Marketing edits them in a dashboard. No deploy required.</p><h2><strong>Let&#8217;s Do the Math: What Does &#8220;Billion Scale&#8221; Actually Mean?</strong></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!3kmW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04bfcf77-522b-41bf-bb59-fbdb35cacb09_1051x468.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3kmW!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04bfcf77-522b-41bf-bb59-fbdb35cacb09_1051x468.png 424w, https://substackcdn.com/image/fetch/$s_!3kmW!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04bfcf77-522b-41bf-bb59-fbdb35cacb09_1051x468.png 848w, https://substackcdn.com/image/fetch/$s_!3kmW!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04bfcf77-522b-41bf-bb59-fbdb35cacb09_1051x468.png 1272w, https://substackcdn.com/image/fetch/$s_!3kmW!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04bfcf77-522b-41bf-bb59-fbdb35cacb09_1051x468.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3kmW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04bfcf77-522b-41bf-bb59-fbdb35cacb09_1051x468.png" width="1051" height="468" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/04bfcf77-522b-41bf-bb59-fbdb35cacb09_1051x468.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:468,&quot;width&quot;:1051,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!3kmW!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04bfcf77-522b-41bf-bb59-fbdb35cacb09_1051x468.png 424w, https://substackcdn.com/image/fetch/$s_!3kmW!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04bfcf77-522b-41bf-bb59-fbdb35cacb09_1051x468.png 848w, https://substackcdn.com/image/fetch/$s_!3kmW!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04bfcf77-522b-41bf-bb59-fbdb35cacb09_1051x468.png 1272w, https://substackcdn.com/image/fetch/$s_!3kmW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F04bfcf77-522b-41bf-bb59-fbdb35cacb09_1051x468.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><strong>War Stories: The Big Ones That Bit Me</strong></h2><h3><strong>Disaster #1: A &#8220;Broadcast to All&#8221; Button Took Down the Whole System</strong></h3><p>Before we built the batch pipeline, someone in marketing clicked &#8220;send to all users.&#8221; Millions of messages flooded the queue in seconds, every worker was saturated, and real-time chat + OTPs were completely blocked for two hours.</p><p><strong>Lesson:</strong> Bulk sends need their own pipeline, strict rate limits, and a big red cancel button.</p><h3><strong>Disaster #2: Third-Party Outage Caused a Retry Avalanche</strong></h3><p>An SMS provider had an outage. Our workers retried furiously, piling up millions of messages in the queue. When the provider came back, expired verification codes went out by the truckload. Users were very confused.</p><p><strong>Lesson:</strong> Circuit breakers + dead letter queues + message TTLs. All three, every time.</p><h3><strong>Disaster #3: Spamming Our Way to Higher Uninstall Rates</strong></h3><p>KPI pressure was high, so we cranked up marketing notifications. Next week&#8217;s metrics? Push opt-out rate up 30%, app uninstalls up too.</p><p><strong>Lesson:</strong> Notifications are a tool, not a harassment vector. Respect your users, play the long game.</p><h3><strong>Disaster #4: Weak Auth Got Us SMS-Farmed</strong></h3><p>Early on, our API auth was loose. Someone scraped our SMS verification endpoint and racked up a terrifying bill.</p><p><strong>Lesson:</strong> Entry rate limiting, per-user frequency caps, fraud checks &#8212; all non-negotiable.</p><h2><strong>Wrapping Up</strong></h2><p>The evolution path is pretty straightforward:</p><pre><code><span>DB + Cron &#8594; Message Queue &#8594; Layered Architecture &#8594; Distributed &amp; Global</span></code></pre><p>Every step gets forced by scale. There&#8217;s no perfect one-size-fits-all architecture you design on day one. You build it as you break it.</p><p>The real challenge isn&#8217;t just &#8220;sending fast&#8221; &#8212; it&#8217;s doing all of this at once:</p><ul><li><p>The right message</p></li><li><p>At the right time</p></li><li><p>Through the right channel</p></li><li><p>To the right person</p></li><li><p>Reliably, at scale</p></li></ul><p>If you can walk through this evolution in an interview, clearly explain what breaks at each stage and how you&#8217;d fix it, and draw a couple of these diagrams? You&#8217;re already ahead of 90% of candidates.</p><p><em>What&#8217;s the worst notification system disaster you&#8217;ve lived through? Drop it in the comments &#8212; I love a good war story.</em></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://seabreeze0828.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">Java, SpringBoot &amp; AI Development: Coding Skills &amp; Interview is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[How Does Redis Handle 100 Million Requests Per Second?]]></title><description><![CDATA[An interview-style deep dive, told the way I&#8217;d explain it across the table.]]></description><link>https://seabreeze0828.substack.com/p/how-does-redis-handle-100-million</link><guid isPermaLink="false">https://seabreeze0828.substack.com/p/how-does-redis-handle-100-million</guid><dc:creator><![CDATA[Dylan Smith]]></dc:creator><pubDate>Sun, 09 Aug 2026 15:37:17 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!h_3j!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6320b5d5-0911-4b08-bf5a-36854a4f5d2f_1277x566.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!h_3j!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6320b5d5-0911-4b08-bf5a-36854a4f5d2f_1277x566.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!h_3j!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6320b5d5-0911-4b08-bf5a-36854a4f5d2f_1277x566.png 424w, https://substackcdn.com/image/fetch/$s_!h_3j!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6320b5d5-0911-4b08-bf5a-36854a4f5d2f_1277x566.png 848w, https://substackcdn.com/image/fetch/$s_!h_3j!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6320b5d5-0911-4b08-bf5a-36854a4f5d2f_1277x566.png 1272w, https://substackcdn.com/image/fetch/$s_!h_3j!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6320b5d5-0911-4b08-bf5a-36854a4f5d2f_1277x566.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!h_3j!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6320b5d5-0911-4b08-bf5a-36854a4f5d2f_1277x566.png" width="1277" height="566" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6320b5d5-0911-4b08-bf5a-36854a4f5d2f_1277x566.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:566,&quot;width&quot;:1277,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!h_3j!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6320b5d5-0911-4b08-bf5a-36854a4f5d2f_1277x566.png 424w, https://substackcdn.com/image/fetch/$s_!h_3j!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6320b5d5-0911-4b08-bf5a-36854a4f5d2f_1277x566.png 848w, https://substackcdn.com/image/fetch/$s_!h_3j!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6320b5d5-0911-4b08-bf5a-36854a4f5d2f_1277x566.png 1272w, https://substackcdn.com/image/fetch/$s_!h_3j!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6320b5d5-0911-4b08-bf5a-36854a4f5d2f_1277x566.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://seabreeze0828.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://seabreeze0828.substack.com/subscribe?"><span>Subscribe now</span></a></p><p>I once watched a production Redis instance push past a million operations per second on a single box, CPU pinned at 60%, and the engineer next to me just shrugged and said, &#8220;Yeah, that&#8217;s nothing for it.&#8221; That moment stuck with me, because it forced me to ask the real question: <strong>what is it about Redis&#8217;s design that lets it scale to 100 million requests per second?</strong></p><p>When I get this question in an interview &#8212; and I&#8217;ve been asked it more than once &#8212; I don&#8217;t start with benchmarks. I start with the thing that confuses everyone first: <strong>Redis&#8217;s core is single-threaded.</strong> How can something single-threaded beat multi-threaded systems? Let me walk through it the way I&#8217;d answer it, layer by layer.</p><h2><strong>1. Why a Single-Threaded Model Is Actually Faster</strong></h2><p>The intuitive answer is &#8220;more threads = more throughput.&#8221; In practice, for an in-memory key-value store, that intuition is wrong. Here&#8217;s why.</p><h3><strong>1.1 The concurrency tax nobody talks about</strong></h3><p>Multi-threaded designs pay three kinds of tax on every operation:</p><p><strong>Lock contention.</strong> The moment two threads can touch the same hash table, you need a mutex. Under high load, threads spend a surprising fraction of their time <em>waiting</em>, not working. And it gets worse with fine-grained locking: more locks mean more complexity, more deadlock risk, and more cache-line ping-pong.</p><p><strong>Context switching.</strong> Every thread switch costs 1&#8211;10 microseconds: saving registers, flushing the TLB, losing the CPU&#8217;s cached view of your data structures. A Redis operation on hot data takes well under a microsecond. If your scheduling overhead is bigger than your actual work, threads are a net loss.</p><p><strong>Cache line invalidation.</strong> This is the silent killer. Modern CPUs read memory in 64-byte cache lines. When core A writes to a variable, core B&#8217;s copy of that cache line is invalidated and must be re-fetched over the interconnect. With eight threads hammering a shared hash table, you get constant <strong>false sharing</strong> &#8212; cores invalidating each other&#8217;s caches over unrelated variables that happen to sit on the same cache line. Throughput collapses not because of any bug, but because of physics.</p><p>Redis sidesteps all three taxes with a blunt instrument: <strong>one thread owns the data.</strong> No locks on the hot path. No contention. The entire dataset lives in L1/L2 cache and stays there.</p><h3><strong>1.2 But wait &#8212; doesn&#8217;t single-threaded mean you can only use one core?</strong></h3><p>That&#8217;s the follow-up I always expect, and it&#8217;s a fair one. The answer is: <em>for the command execution path, yes &#8212; and that&#8217;s fine.</em> Redis&#8217;s bottleneck is almost never CPU compute; it&#8217;s network I/O and memory bandwidth. And when you do need more cores, you scale <strong>out</strong> (more instances, Redis Cluster) rather than up with threads. We&#8217;ll get to that.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!xVzB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cccddd1-541f-496c-a31a-1eb6f788b2aa_1313x241.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!xVzB!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cccddd1-541f-496c-a31a-1eb6f788b2aa_1313x241.png 424w, https://substackcdn.com/image/fetch/$s_!xVzB!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cccddd1-541f-496c-a31a-1eb6f788b2aa_1313x241.png 848w, https://substackcdn.com/image/fetch/$s_!xVzB!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cccddd1-541f-496c-a31a-1eb6f788b2aa_1313x241.png 1272w, https://substackcdn.com/image/fetch/$s_!xVzB!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cccddd1-541f-496c-a31a-1eb6f788b2aa_1313x241.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!xVzB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cccddd1-541f-496c-a31a-1eb6f788b2aa_1313x241.png" width="1313" height="241" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8cccddd1-541f-496c-a31a-1eb6f788b2aa_1313x241.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:241,&quot;width&quot;:1313,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!xVzB!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cccddd1-541f-496c-a31a-1eb6f788b2aa_1313x241.png 424w, https://substackcdn.com/image/fetch/$s_!xVzB!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cccddd1-541f-496c-a31a-1eb6f788b2aa_1313x241.png 848w, https://substackcdn.com/image/fetch/$s_!xVzB!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cccddd1-541f-496c-a31a-1eb6f788b2aa_1313x241.png 1272w, https://substackcdn.com/image/fetch/$s_!xVzB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8cccddd1-541f-496c-a31a-1eb6f788b2aa_1313x241.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>The mental model I use: <strong>Redis trades parallelism for the elimination of synchronization.</strong> When your critical section is the entire dataset, the cheapest lock is the one you never need.</p><h2><strong>2. The Reactor Pattern and I/O Multiplexing</strong></h2><p>So how does one thread juggle tens of thousands of client connections? This is where the answer gets interesting: Redis is a textbook <strong>Reactor pattern</strong> implementation built on I/O multiplexing &#8212; <code>epoll</code> on Linux, <code>kqueue</code> on BSD/macOS.</p><h3><strong>2.1 The key insight: don&#8217;t block, register</strong></h3><p>A naive server reads from a socket and <em>blocks</em> until data arrives. With 10,000 clients, that&#8217;s 10,000 blocked threads. Redis instead asks the kernel: <em>&#8220;tell me when any of these 10,000 sockets is actually ready,&#8221;</em> and only then processes it. The single thread never waits on I/O &#8212; it waits on the <strong>event multiplexer</strong>, which returns only sockets that have work to do.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!GSVf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe0d4dd4-af3b-4bac-92fa-b1a1629eafdc_1313x651.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!GSVf!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe0d4dd4-af3b-4bac-92fa-b1a1629eafdc_1313x651.png 424w, https://substackcdn.com/image/fetch/$s_!GSVf!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe0d4dd4-af3b-4bac-92fa-b1a1629eafdc_1313x651.png 848w, https://substackcdn.com/image/fetch/$s_!GSVf!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe0d4dd4-af3b-4bac-92fa-b1a1629eafdc_1313x651.png 1272w, https://substackcdn.com/image/fetch/$s_!GSVf!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe0d4dd4-af3b-4bac-92fa-b1a1629eafdc_1313x651.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!GSVf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe0d4dd4-af3b-4bac-92fa-b1a1629eafdc_1313x651.png" width="1313" height="651" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fe0d4dd4-af3b-4bac-92fa-b1a1629eafdc_1313x651.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:651,&quot;width&quot;:1313,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!GSVf!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe0d4dd4-af3b-4bac-92fa-b1a1629eafdc_1313x651.png 424w, https://substackcdn.com/image/fetch/$s_!GSVf!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe0d4dd4-af3b-4bac-92fa-b1a1629eafdc_1313x651.png 848w, https://substackcdn.com/image/fetch/$s_!GSVf!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe0d4dd4-af3b-4bac-92fa-b1a1629eafdc_1313x651.png 1272w, https://substackcdn.com/image/fetch/$s_!GSVf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffe0d4dd4-af3b-4bac-92fa-b1a1629eafdc_1313x651.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3><strong>2.2 Inside </strong><code>aeMain()</code><strong> &#8212; the heart of Redis</strong></h3><p>Every Redis instance spends its life in one function, <code>aeMain()</code>. If I had to whiteboard it in an interview, this is what I&#8217;d draw:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!PJso!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd214b6aa-25f9-4e04-af89-645e38f46215_1217x956.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!PJso!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd214b6aa-25f9-4e04-af89-645e38f46215_1217x956.png 424w, https://substackcdn.com/image/fetch/$s_!PJso!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd214b6aa-25f9-4e04-af89-645e38f46215_1217x956.png 848w, https://substackcdn.com/image/fetch/$s_!PJso!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd214b6aa-25f9-4e04-af89-645e38f46215_1217x956.png 1272w, https://substackcdn.com/image/fetch/$s_!PJso!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd214b6aa-25f9-4e04-af89-645e38f46215_1217x956.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!PJso!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd214b6aa-25f9-4e04-af89-645e38f46215_1217x956.png" width="1217" height="956" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d214b6aa-25f9-4e04-af89-645e38f46215_1217x956.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:956,&quot;width&quot;:1217,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!PJso!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd214b6aa-25f9-4e04-af89-645e38f46215_1217x956.png 424w, https://substackcdn.com/image/fetch/$s_!PJso!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd214b6aa-25f9-4e04-af89-645e38f46215_1217x956.png 848w, https://substackcdn.com/image/fetch/$s_!PJso!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd214b6aa-25f9-4e04-af89-645e38f46215_1217x956.png 1272w, https://substackcdn.com/image/fetch/$s_!PJso!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd214b6aa-25f9-4e04-af89-645e38f46215_1217x956.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>And here&#8217;s the core logic as C pseudocode &#8212; close enough to <code>ae.c</code> to be honest, simplified enough to read in thirty seconds:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;c&quot;,&quot;nodeId&quot;:&quot;b31417d4-87a9-42cf-b8b2-9c086439de73&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-c">/* Simplified version of Redis&#8217;s ae.c event loop */
void aeMain(aeEventLoop *eventLoop) {
    eventLoop-&gt;stop = 0;
    while (!eventLoop-&gt;stop) {
        aeProcessEvents(eventLoop, AE_ALL_EVENTS | AE_CALL_BEFORE_SLEEP | AE_CALL_AFTER_SLEEP);
    }
}

int aeProcessEvents(aeEventLoop *eventLoop, int flags) {
    /* 1. Find the nearest time event to bound the poll timeout */
    aeTimeEvent *shortest = aeSearchNearestTimer(eventLoop);
    struct timeval tvp = timeUntilNextTimer(shortest);
    /* 2. Ask the kernel: which sockets are ready RIGHT NOW? */
    int numevents = aeApiPoll(eventLoop, &amp;tvp);   /* epoll_wait / kevent under the hood */
    /* 3. Handle only the sockets that actually have work */
    for (int j = 0; j &lt; numevents; j++) {
        aeFileEvent *fe = &amp;eventLoop-&gt;events[eventLoop-&gt;fired[j].fd];
        if (fe-&gt;mask &amp; AE_READABLE) fe-&gt;rfileProc(eventLoop, fd, ...);  /* readQueryFromClient */
        if (fe-&gt;mask &amp; AE_WRITABLE) fe-&gt;wfileProc(eventLoop, fd, ...);  /* sendReplyToClient */
    }
    /* 4. Run due time events: incremental rehash, expiry scans, cron */
    processTimeEvents(eventLoop);
    return numevents;
}</code></pre></div><p>The details worth mentioning in an interview:</p><ul><li><p>On Linux, Redis uses <code>epoll</code> in <strong>edge-triggered</strong> mode with an event array, so the cost of polling is O(ready events), not O(total connections). <code>select()</code>/<code>poll()</code> are O(n) per call &#8212; that&#8217;s why <code>epoll</code> matters at scale.</p></li><li><p><strong>Time events are never late by more than one loop iteration</strong>, because the poll timeout is always bounded by the nearest timer. That&#8217;s how <code>activeExpireCycle</code> runs smoothly alongside client traffic.</p></li><li><p>Even incremental rehash (covered next) gets a slice of CPU here &#8212; a few milliseconds per loop, never a big pause.</p></li></ul><p>One sentence I like to say out loud at this point: <strong>&#8220;Redis isn&#8217;t fast despite being single-threaded &#8212; it&#8217;s fast because the single thread never does anything slow: it never blocks, never locks, and never context-switches.&#8221;</strong></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://seabreeze0828.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">Java, SpringBoot &amp; AI Development: Coding Skills &amp; Interview is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2><strong>3. In-Memory Data Structures Built for Speed</strong></h2><p>The event loop explains how Redis <em>handles</em> requests. The data structures explain why each request is so <em>cheap</em>. Two of my favorites: <strong>SDS</strong> and the <strong>dict with progressive rehash</strong>.</p><h3><strong>3.1 SDS &#8212; Simple Dynamic String</strong></h3><p>Redis doesn&#8217;t use C strings. It rolls its own: the SDS. The definition is small, but every field is a deliberate performance choice:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;c&quot;,&quot;nodeId&quot;:&quot;f3c28433-bb66-47c6-8fdc-61c7cabb73ab&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-c">/* Redis SDS (simplified; real version has sdshdr5/8/16/32/64 variants) */
struct sdshdr {
    int len;      /* used length  -&gt; O(1) strlen, no scanning for &#8216;\0&#8217; */
    int free;     /* unused capacity -&gt; amortized O(1) append */
    char buf[];   /* flexible array holding the actual bytes */
};</code></pre></div><p>Why this matters in practice:</p><ol><li><p><strong>O(1) length queries.</strong> <code>STRLEN</code> on a C string scans for the null terminator &#8212; O(n). On a 1 MB value, that&#8217;s the difference between a nanosecond and a millisecond.</p></li><li><p><strong>No buffer overflows.</strong> <code>strcat</code> trusts you; SDS checks <code>free</code> and reallocates itself.</p></li><li><p><strong>Space pre-allocation.</strong> Appends under 1 MB double the buffer, so repeated <code>APPEND</code> is amortized O(1) instead of reallocating on every write.</p></li><li><p><strong>Binary-safe.</strong> <code>len</code> defines the string, not a null byte &#8212; you can store images, protobuf, whatever.</p></li><li><p><strong>Five header sizes</strong> (<code>sdshdr5</code> through <code>sdshdr64</code>) mean a 3-byte key pays for a 1-byte length field, not an 8-byte one. At billions of keys, that&#8217;s gigabytes saved.</p></li></ol><h3><strong>3.2 The dict: hash table + incremental rehash</strong></h3><p>Redis&#8217;s core keyspace is the <code>dict</code> &#8212; chained-hash tables with MurmurHash/FNV and <strong>incremental rehashing</strong>. The classic problem: when a hash table doubles in size, rehashing millions of keys in one shot would freeze the single thread for hundreds of milliseconds. Unacceptable.</p><p>Redis&#8217;s answer: <strong>spread the rehash across many small steps</strong>, doing a little bit of work on every access.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!bjaP!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25162a8d-7352-4b00-a7dd-123ca5f33d3c_1313x637.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!bjaP!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25162a8d-7352-4b00-a7dd-123ca5f33d3c_1313x637.png 424w, https://substackcdn.com/image/fetch/$s_!bjaP!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25162a8d-7352-4b00-a7dd-123ca5f33d3c_1313x637.png 848w, https://substackcdn.com/image/fetch/$s_!bjaP!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25162a8d-7352-4b00-a7dd-123ca5f33d3c_1313x637.png 1272w, https://substackcdn.com/image/fetch/$s_!bjaP!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25162a8d-7352-4b00-a7dd-123ca5f33d3c_1313x637.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!bjaP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25162a8d-7352-4b00-a7dd-123ca5f33d3c_1313x637.png" width="1313" height="637" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/25162a8d-7352-4b00-a7dd-123ca5f33d3c_1313x637.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:637,&quot;width&quot;:1313,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!bjaP!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25162a8d-7352-4b00-a7dd-123ca5f33d3c_1313x637.png 424w, https://substackcdn.com/image/fetch/$s_!bjaP!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25162a8d-7352-4b00-a7dd-123ca5f33d3c_1313x637.png 848w, https://substackcdn.com/image/fetch/$s_!bjaP!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25162a8d-7352-4b00-a7dd-123ca5f33d3c_1313x637.png 1272w, https://substackcdn.com/image/fetch/$s_!bjaP!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F25162a8d-7352-4b00-a7dd-123ca5f33d3c_1313x637.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;rust&quot;,&quot;nodeId&quot;:&quot;5d19877e-e260-4d45-a669-23c6d0c5b47c&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-rust">/* Simplified from dict.c &#8212; the progressive rehash contract */
typedef struct dict {
    dictht ht[2];          /* two tables: source and destination */
    long rehashidx;        /* -1 = not rehashing; else next bucket to move */
} dict;

/* Called incrementally: move `n` buckets, never more */
void dictRehash(dict *d, int n) {
    while (n-- &amp;&amp; d-&gt;ht[0].used != 0) {
        /* find next non-empty bucket */
        while (d-&gt;ht[0].table[d-&gt;rehashidx] == NULL) d-&gt;rehashidx++;
        /* move all entries in this bucket to ht[1] */
        migrateEntries(d-&gt;ht[0].table[d-&gt;rehashidx], &amp;d-&gt;ht[1]);
        d-&gt;rehashidx++;
    }
    if (d-&gt;ht[0].used == 0) { ht[1] becomes ht[0]; rehashidx = -1; } /* done */
}

/* Every lookup/delete/insert helps a little while rehash is active */
dictEntry *dictFind(dict *d, const void *key) {
    if (dictIsRehashing(d)) dictRehash(d, 1);   /* pay one step of the toll */
    /* search ht[0], then ht[1] */
}</code></pre></div><p>The beauty is in the amortization: no single operation ever pays the full rehash cost. Worst-case latency stays flat while the table grows from 1M to 100M keys. Same idea powers <code>KEYS</code> alternatives and why <code>SCAN</code> exists &#8212; Redis consistently refuses to do big things all at once.</p><h2><strong>4. Pipelining: Killing the RTT Bottleneck</strong></h2><p>Now let&#8217;s leave the server and look at the client side &#8212; because I&#8217;ve profiled plenty of &#8220;slow Redis&#8221; problems that turned out to be pure network latency.</p><p>Here&#8217;s the trap: on a 0.5 ms RTT network, sending 100,000 commands one at a time costs <strong>50 seconds of pure round trips</strong>, even if Redis executes each in 10 microseconds. The bottleneck isn&#8217;t Redis; it&#8217;s physics.</p><p><strong>Pipelining</strong> flips the model: send a batch of commands without waiting for replies, then read all replies at once. RTT is paid once, not N times.</p><h3><strong>4.1 Wrong way vs. right way</strong></h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;e0ef6cf5-00f4-45ae-ab24-bb3d8d3ceee9&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">import redis.clients.jedis.Jedis;
import redis.clients.jedis.Pipeline;

public class PipelineDemo {
    public static void main(String[] args) {
        try (Jedis jedis = new Jedis(&#8221;localhost&#8221;, 6379)) {
            int N = 100_000;

            // &#10060; Wrong: one round trip per command
            // With 0.5ms RTT this takes ~50 seconds no matter how fast Redis is.
            long start = System.nanoTime();
            for (int i = 0; i &lt; N; i++) {
                jedis.set(&#8221;key:&#8221; + i, String.valueOf(i)); // send -&gt; WAIT for reply -&gt; next
            }
            System.out.printf(&#8221;naive:    %.2fs%n&#8221;, (System.nanoTime() - start) / 1e9);

            // &#9989; Right: pipeline batches commands, one round trip per batch
            start = System.nanoTime();
            Pipeline pipe = jedis.pipelined();   // pipeline != MULTI/EXEC
            for (int i = 0; i &lt; N; i++) {
                pipe.set(&#8221;key:&#8221; + i, String.valueOf(i));
            }
            pipe.sync();                         // one big send, one big read
            System.out.printf(&#8221;pipeline: %.2fs%n&#8221;, (System.nanoTime() - start) / 1e9);
        }
    }
}</code></pre></div><p>On a typical cross-AZ link, I&#8217;ve seen this turn 45 seconds into 1.2 seconds &#8212; a ~40x win with zero server-side changes.</p><p>Two things I always add:</p><ul><li><p><strong>Size your batches.</strong> A pipeline of 100k commands builds a large output buffer on the server. Batches of 1,000&#8211;10,000 are usually the sweet spot between throughput and memory.</p></li><li><p><strong>Pipeline &#8800; transaction.</strong> A pipeline just batches I/O; commands can interleave with other clients&#8217;. Use MULTI/EXEC (e.g., Jedis&#8217;s `Transaction`) only when you need atomicity.</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_!ICe2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c0187d0-fca2-4cc0-9078-2fa3c26b527b_1313x656.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ICe2!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c0187d0-fca2-4cc0-9078-2fa3c26b527b_1313x656.png 424w, https://substackcdn.com/image/fetch/$s_!ICe2!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c0187d0-fca2-4cc0-9078-2fa3c26b527b_1313x656.png 848w, https://substackcdn.com/image/fetch/$s_!ICe2!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c0187d0-fca2-4cc0-9078-2fa3c26b527b_1313x656.png 1272w, https://substackcdn.com/image/fetch/$s_!ICe2!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c0187d0-fca2-4cc0-9078-2fa3c26b527b_1313x656.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ICe2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c0187d0-fca2-4cc0-9078-2fa3c26b527b_1313x656.png" width="1313" height="656" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4c0187d0-fca2-4cc0-9078-2fa3c26b527b_1313x656.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:656,&quot;width&quot;:1313,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!ICe2!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c0187d0-fca2-4cc0-9078-2fa3c26b527b_1313x656.png 424w, https://substackcdn.com/image/fetch/$s_!ICe2!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c0187d0-fca2-4cc0-9078-2fa3c26b527b_1313x656.png 848w, https://substackcdn.com/image/fetch/$s_!ICe2!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c0187d0-fca2-4cc0-9078-2fa3c26b527b_1313x656.png 1272w, https://substackcdn.com/image/fetch/$s_!ICe2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4c0187d0-fca2-4cc0-9078-2fa3c26b527b_1313x656.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><strong>5. Scaling to 100M RPS: Redis Cluster</strong></h2><p>A single Redis instance tops out around 1M+ ops/s on modern hardware (more with threaded I/O, next section). To reach 100 million, we scale horizontally with <strong>Redis Cluster</strong>: 16,384 hash slots distributed across masters, each master with replicas for failover.</p><p>Rough math I&#8217;d share in an interview: 100 shards &#215; ~1M ops/s each &#8776; 100M RPS, with headroom. The cluster isn&#8217;t magic &#8212; it&#8217;s partitioning plus a gossip protocol.</p><h3><strong>5.1 Sharding topology</strong></h3><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!1WdC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88cd09df-af82-4cd6-b9de-50a82faab988_1313x592.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1WdC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88cd09df-af82-4cd6-b9de-50a82faab988_1313x592.png 424w, https://substackcdn.com/image/fetch/$s_!1WdC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88cd09df-af82-4cd6-b9de-50a82faab988_1313x592.png 848w, https://substackcdn.com/image/fetch/$s_!1WdC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88cd09df-af82-4cd6-b9de-50a82faab988_1313x592.png 1272w, https://substackcdn.com/image/fetch/$s_!1WdC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88cd09df-af82-4cd6-b9de-50a82faab988_1313x592.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!1WdC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88cd09df-af82-4cd6-b9de-50a82faab988_1313x592.png" width="1313" height="592" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/88cd09df-af82-4cd6-b9de-50a82faab988_1313x592.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:592,&quot;width&quot;:1313,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!1WdC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88cd09df-af82-4cd6-b9de-50a82faab988_1313x592.png 424w, https://substackcdn.com/image/fetch/$s_!1WdC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88cd09df-af82-4cd6-b9de-50a82faab988_1313x592.png 848w, https://substackcdn.com/image/fetch/$s_!1WdC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88cd09df-af82-4cd6-b9de-50a82faab988_1313x592.png 1272w, https://substackcdn.com/image/fetch/$s_!1WdC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F88cd09df-af82-4cd6-b9de-50a82faab988_1313x592.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>How keys land on slots: <code>CRC16(key) mod 16384</code>. Here&#8217;s the Java version:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;b16a6403-fc68-400f-a21c-e16f26e7434f&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">import java.nio.charset.StandardCharsets;

public class SlotCalculator {
    /**
     * Redis Cluster slot for a key.
     *
     * Honors hash tags: &#8216;{user:1}.profile&#8217; hashes only &#8216;user:1&#8217;,
     * letting you force related keys onto the same slot (e.g. for MULTI).
     */
    public static int keySlot(String key) {
        int s = key.indexOf(&#8217;{&#8217;), e = key.indexOf(&#8217;}&#8217;);
        if (s != -1 &amp;&amp; e != -1 &amp;&amp; e &gt; s + 1) {
            key = key.substring(s + 1, e);                      // hash tag wins
        }
        return crc16(key.getBytes(StandardCharsets.UTF_8)) % 16384; // XMODEM CRC16
    }

    private static int crc16(byte[] data) {
        int crc = 0;
        for (byte b : data) {
            crc ^= (b &amp; 0xFF) &lt;&lt; 8;
            for (int i = 0; i &lt; 8; i++) {
                crc = (crc &amp; 0x8000) != 0 ? (crc &lt;&lt; 1) ^ 0x1021 : crc &lt;&lt; 1;
                crc &amp;= 0xFFFF;
            }
        }
        return crc;
    }

    public static void main(String[] args) {
        System.out.println(keySlot(&#8221;user:1000&#8221;));            // e.g. 6918
        System.out.println(keySlot(&#8221;{user:1000}.profile&#8221;));  // same slot as &#8216;user:1000&#8217;
    }
}</code></pre></div><p>And standing up a 3-master/3-replica cluster from the CLI:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;bash&quot;,&quot;nodeId&quot;:&quot;fb37c927-8769-45b8-8d4c-5d0790b85de0&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-bash"># 1. Launch six nodes (ports 7000-7005), each with cluster-enabled yes
for port in $(seq 7000 7005); do
  redis-server ./nodes/$port/redis.conf &amp;
done

# 2. Create the cluster: 3 masters, 1 replica each, slots auto-assigned
redis-cli --cluster create \
  127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 \
  127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 \
  --cluster-replicas 1
# 3. Verify slot distribution and health
redis-cli --cluster check 127.0.0.1:7000</code></pre></div><p>Three operational points worth saying in an interview:</p><ol><li><p><strong>Clients must be cluster-aware.</strong> Smart clients (e.g., `JedisCluster`, Lettuce) cache the slot&#8594;node map and talk to the right master directly. A `MOVED` reply just means &#8220;refresh your map.&#8221;</p></li><li><p><strong>No multi-key across slots.</strong> Multi-key commands spanning slots fail unless the keys share a hash tag &#8212; that&#8217;s the trade-off of partitioning.</p></li><li><p><strong>Failover is automatic.</strong> If a master dies, its replicas elect a replacement via gossip-based voting, with no external sentinel required.</p></li></ol><h2><strong>6. The Multi-Threaded I/O Evolution (Redis 6.0 &#8594; 7.x)</strong></h2><p>Here&#8217;s the part that surprises people, and it&#8217;s a great way to close the answer: <strong>Redis added threads &#8212; but only where they actually help.</strong></p><p>Redis 6.0 introduced <strong>multi-threaded I/O</strong>: socket reading, protocol parsing, and response writing can be offloaded to I/O threads, while <em>command execution stays strictly single-threaded</em>. Redis 7.x carried this forward with refinements, and the config became genuinely practical at high connection counts.</p><p>Why this split? Profiling showed that at very high throughput, the bottleneck wasn&#8217;t executing commands &#8212; it was <strong>syscalls and string parsing of the RESP protocol</strong>. So Redis parallelized the cheap-to-parallelize part (I/O) and kept the hard-to-parallelize part (data access) lock-free.</p><pre><code><span># redis.conf &#8212; threaded I/O
io-threads 4           # I/O threads; rule of thumb: ~ half the core count
io-threads-do-reads yes</span></code></pre><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!BH8Y!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa245ab6-5888-4157-afdf-14a8e3d980eb_1313x136.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!BH8Y!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa245ab6-5888-4157-afdf-14a8e3d980eb_1313x136.png 424w, https://substackcdn.com/image/fetch/$s_!BH8Y!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa245ab6-5888-4157-afdf-14a8e3d980eb_1313x136.png 848w, https://substackcdn.com/image/fetch/$s_!BH8Y!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa245ab6-5888-4157-afdf-14a8e3d980eb_1313x136.png 1272w, https://substackcdn.com/image/fetch/$s_!BH8Y!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa245ab6-5888-4157-afdf-14a8e3d980eb_1313x136.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!BH8Y!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa245ab6-5888-4157-afdf-14a8e3d980eb_1313x136.png" width="1313" height="136" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/aa245ab6-5888-4157-afdf-14a8e3d980eb_1313x136.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:136,&quot;width&quot;:1313,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!BH8Y!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa245ab6-5888-4157-afdf-14a8e3d980eb_1313x136.png 424w, https://substackcdn.com/image/fetch/$s_!BH8Y!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa245ab6-5888-4157-afdf-14a8e3d980eb_1313x136.png 848w, https://substackcdn.com/image/fetch/$s_!BH8Y!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa245ab6-5888-4157-afdf-14a8e3d980eb_1313x136.png 1272w, https://substackcdn.com/image/fetch/$s_!BH8Y!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa245ab6-5888-4157-afdf-14a8e3d980eb_1313x136.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>The lifecycle of one event-loop iteration with threaded I/O:</p><ol><li><p>Main thread collects ready reads, <strong>distributes them across I/O threads</strong>.</p></li><li><p>I/O threads read and parse requests in parallel; main thread waits (spin) until done.</p></li><li><p>Main thread executes all commands sequentially &#8212; the data model stays trivially safe.</p></li><li><p>Replies are <strong>distributed back to I/O threads</strong> for parallel writes.</p></li></ol><p>The honest numbers I&#8217;d quote: with 4&#8211;8 I/O threads, throughput roughly <strong>doubles</strong> on network-bound workloads, with no change in the consistency model. It&#8217;s not a 4x win, because execution is still serial &#8212; and that&#8217;s the design point. Redis grew threads <em>surgically</em>, exactly where profiling said to.</p><h2><strong>7. Key Takeaways</strong></h2><p>If I had one minute left in the interview, this is the summary I&#8217;d give:</p><ol><li><p><strong>Single-threaded is a feature, not a limitation.</strong> By eliminating lock contention, context switches, and cache-line invalidation, Redis&#8217;s hot path runs in microseconds with zero synchronization cost.</p></li><li><p><strong>The Reactor pattern + I/O multiplexing</strong> (<code>epoll</code>/<code>kqueue</code>) let one thread serve tens of thousands of connections by processing only sockets that are actually ready &#8212; the <code>aeMain()</code> loop never blocks on any single client.</p></li><li><p><strong>Purpose-built data structures keep every operation cheap:</strong> SDS gives O(1) length checks and amortized O(1) appends; the dict&#8217;s incremental rehash spreads table resizing across millions of operations so latency never spikes.</p></li><li><p><strong>Pipelining removes the network round trip from the critical path</strong> &#8212; often a 10&#8211;50x client-side speedup with zero server changes. If your Redis feels slow, check the client first.</p></li><li><p><strong>100M RPS comes from horizontal scaling.</strong> Redis Cluster splits keyspace into 16,384 slots (<code>CRC16(key) % 16384</code>) across many masters with automatic replica failover; ~100 shards &#215; ~1M ops/s gets you there.</p></li><li><p><strong>Threaded I/O (6.0+, refined in 7.x) shows pragmatic evolution:</strong> parallelize socket I/O where profiling proved the bottleneck, keep command execution single-threaded to preserve the lock-free data model.</p></li></ol><p>The deeper lesson, the one I&#8217;d want the interviewer to walk away with: Redis isn&#8217;t fast because of any single trick. It&#8217;s fast because every layer of the design &#8212; threading model, event loop, memory layout, wire protocol, cluster topology &#8212; was engineered to <strong>remove work rather than optimize it</strong>. The fastest instruction is the one you never execute; the fastest lock is the one that doesn&#8217;t exist.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://seabreeze0828.substack.com/?utm_source=substack&amp;utm_medium=email&amp;utm_content=share&amp;action=share&quot;,&quot;text&quot;:&quot;Share Java, SpringBoot &amp; AI Development: Coding Skills &amp; Interview&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://seabreeze0828.substack.com/?utm_source=substack&amp;utm_medium=email&amp;utm_content=share&amp;action=share"><span>Share Java, SpringBoot &amp; AI Development: Coding Skills &amp; Interview</span></a></p><p></p>]]></content:encoded></item><item><title><![CDATA[Interview: Bloom Filter vs Cuckoo Filter for a One-Billion-User System]]></title><description><![CDATA[One billion usernames in 1.2 GB &#8212; Bloom or Cuckoo? Interviewers ask exactly this.]]></description><link>https://seabreeze0828.substack.com/p/interview-bloom-filter-vs-cuckoo</link><guid isPermaLink="false">https://seabreeze0828.substack.com/p/interview-bloom-filter-vs-cuckoo</guid><dc:creator><![CDATA[Dylan Smith]]></dc:creator><pubDate>Sat, 08 Aug 2026 03:00:15 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!g-DH!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc53e5f02-1e4d-41e4-8aff-05b5f86e9824_1500x967.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!g-DH!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc53e5f02-1e4d-41e4-8aff-05b5f86e9824_1500x967.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!g-DH!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc53e5f02-1e4d-41e4-8aff-05b5f86e9824_1500x967.jpeg 424w, https://substackcdn.com/image/fetch/$s_!g-DH!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc53e5f02-1e4d-41e4-8aff-05b5f86e9824_1500x967.jpeg 848w, https://substackcdn.com/image/fetch/$s_!g-DH!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc53e5f02-1e4d-41e4-8aff-05b5f86e9824_1500x967.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!g-DH!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc53e5f02-1e4d-41e4-8aff-05b5f86e9824_1500x967.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!g-DH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc53e5f02-1e4d-41e4-8aff-05b5f86e9824_1500x967.jpeg" width="1456" height="939" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c53e5f02-1e4d-41e4-8aff-05b5f86e9824_1500x967.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:939,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!g-DH!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc53e5f02-1e4d-41e4-8aff-05b5f86e9824_1500x967.jpeg 424w, https://substackcdn.com/image/fetch/$s_!g-DH!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc53e5f02-1e4d-41e4-8aff-05b5f86e9824_1500x967.jpeg 848w, https://substackcdn.com/image/fetch/$s_!g-DH!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc53e5f02-1e4d-41e4-8aff-05b5f86e9824_1500x967.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!g-DH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc53e5f02-1e4d-41e4-8aff-05b5f86e9824_1500x967.jpeg 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Interviewers often open with this one: how do you check whether a username already exists among one billion users? In the previous article we gave the cheapest answer, the Bloom filter. At a 1% false positive rate each element needs about 9.6 bits, so a table of one billion usernames fits in 1.2 GB of memory. Push the target down to 0.1% and each element climbs to 14.4 bits, about 1.8 GB, still within reach of a single machine.</p><p>So what is the Cuckoo filter for? Because lists get deleted. Usernames almost never disappear, but blacklists and device lists lose entries all the time, and the Bloom filter has no delete operation.</p><p>The database stays the source of truth. The filter is just a gate in front of it. Bloom or Cuckoo comes down to one question: will this set lose entries?</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://seabreeze0828.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://seabreeze0828.substack.com/subscribe?"><span>Subscribe now</span></a></p><h2><strong>Understanding the Challenge</strong></h2><p>Checking a username against one billion existing users packs several problems into one path.</p><p>The set is too large to scan. On the registration hot path, a linear pass over one billion rows is out of the question.</p><p>This check runs right in front of the user. Every registration needs it, so does most login traffic, and it has to come back in single-digit milliseconds. Slower than that and the product starts to feel laggy.</p><p>Memory matters just as much. A hash set of one billion usernames, ten bytes each on average, runs to tens of gigabytes. You need a structure far smaller than the raw data to answer the membership question.</p><p>Usernames almost never disappear, but other lists do. Blacklists get unbanned, device lists expire, rate-limit lists get cleared. Once the filter cannot remove entries, the only option is a full rebuild.</p><p>Two users can race for the same username. The filter tells both &#8220;probably available&#8221;, and something downstream still has to guarantee that only one registration succeeds.</p><h2><strong>The Baseline: Database Design</strong></h2><p>No matter what filter sits in front, the database stays the source of truth. The schema from the previous article still works.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:&quot;b735be82-2922-4a71-b5e9-f5b90298aba9&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">CREATE TABLE users (
    user_id BIGINT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(255) UNIQUE NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
</code></pre></div><p>The unique index on <code>username</code> is not optional. It is the atomic gate that blocks duplicates when two requests race. A plain B-tree index handles point lookups fine, and the unique constraint doubles as the concurrency backstop.</p><p>The most direct way is to query the database once.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;sql&quot;,&quot;nodeId&quot;:&quot;35cb9fc2-0bbb-4530-9d7f-38e9762f9424&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-sql">SELECT COUNT(*) FROM users WHERE username = 'desiredUsername';</code></pre></div><p>Or as a prepared statement.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;07c6a591-9797-4eec-af82-8a02b3669a9b&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">boolean exists = jdbcTemplate.queryForObject(
    "SELECT COUNT(*) FROM users WHERE username = ?",
    Boolean.class, username);
</code></pre></div><p>Both work, and both cost too much on the hot path. At one billion rows, every check walks the index and fetches data, burning database CPU even when the answer is obvious. The database should only see the cases the filter cannot decide.</p><h2><strong>Step One: Put a Bloom Filter in Front</strong></h2><p>A Bloom filter answers one binary question: is this element in the set? It maps the element to k positions in a bit array with k hash functions. All k positions must be 1 for the answer to be &#8220;possibly present&#8221;. If any position is 0, the element is definitely not there.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!n_UE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89a48ba2-5629-41e5-b709-eec70b451d70_2132x1025.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!n_UE!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89a48ba2-5629-41e5-b709-eec70b451d70_2132x1025.png 424w, https://substackcdn.com/image/fetch/$s_!n_UE!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89a48ba2-5629-41e5-b709-eec70b451d70_2132x1025.png 848w, https://substackcdn.com/image/fetch/$s_!n_UE!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89a48ba2-5629-41e5-b709-eec70b451d70_2132x1025.png 1272w, https://substackcdn.com/image/fetch/$s_!n_UE!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89a48ba2-5629-41e5-b709-eec70b451d70_2132x1025.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!n_UE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89a48ba2-5629-41e5-b709-eec70b451d70_2132x1025.png" width="1456" height="700" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/89a48ba2-5629-41e5-b709-eec70b451d70_2132x1025.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:700,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:217061,&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://seabreeze0828.substack.com/i/210301838?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89a48ba2-5629-41e5-b709-eec70b451d70_2132x1025.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_!n_UE!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89a48ba2-5629-41e5-b709-eec70b451d70_2132x1025.png 424w, https://substackcdn.com/image/fetch/$s_!n_UE!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89a48ba2-5629-41e5-b709-eec70b451d70_2132x1025.png 848w, https://substackcdn.com/image/fetch/$s_!n_UE!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89a48ba2-5629-41e5-b709-eec70b451d70_2132x1025.png 1272w, https://substackcdn.com/image/fetch/$s_!n_UE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F89a48ba2-5629-41e5-b709-eec70b451d70_2132x1025.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>That asymmetry is the point. It never produces false negatives, it can produce false positives, and the rate is tunable. With a bit array of size m and k hash functions, the false positive rate is about</p><pre><code><code>(1 - e^(-kn/m))^k</code></code></pre><p>The optimal number of hash functions is k = (m/n) * ln(2). At a 1% false positive rate, m/n lands around 9.6 bits and k is 7. At 0.1%, m/n is about 14.4 bits and k is about 10. Element length does not matter: one billion entries at 1% cost a fixed 1.2 GB, whether the usernames are 6 characters or 60.</p><h3><strong>A Minimal Java Implementation</strong></h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;b82d9b57-c02e-41e5-a1eb-634124a35b03&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">import java.util.BitSet;

public class SimpleBloomFilter {
    private final BitSet bits;
    private final int size;
    private final int hashCount;

    public SimpleBloomFilter(int size, int hashCount) {
        this.bits = new BitSet(size);
        this.size = size;
        this.hashCount = hashCount;
    }

    private int[] positions(String value) {
        int[] pos = new int[hashCount];
        int hash1 = value.hashCode();
        int hash2 = (hash1 &gt;&gt;&gt; 16) | (hash1 &lt;&lt; 16);
        for (int i = 0; i &lt; hashCount; i++) {
            pos[i] = Math.floorMod(hash1 + i * hash2, size);
        }
        return pos;
    }

    public void add(String value) {
        for (int p : positions(value)) {
            bits.set(p);
        }
    }

    public boolean mightContain(String value) {
        for (int p : positions(value)) {
            if (!bits.get(p)) {
                return false;   // one bit is 0, definitely absent
            }
        }
        return true;            // all bits are 1, possibly present
    }
}</code></pre></div><p>Don&#8217;t write this yourself in production. Guava&#8217;s <code>BloomFilter</code> handles sizing, hashing, and serialization.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;45ac22f2-ddd4-4233-bb29-8b213812dfee&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">BloomFilter&lt;String&gt; takenUsernames = BloomFilter.create(
        Funnels.stringFunnel(StandardCharsets.UTF_8),
        1_000_000_000L,   // expected insertions
        0.01);            // target false positive rate

takenUsernames.put("alice");
boolean taken = takenUsernames.mightContain("alice");</code></pre></div><p>Redis exposes the same operations as commands.</p><pre><code><code>BF.RESERVE usernames 0.01 1000000000
BF.ADD usernames alice
BF.EXISTS usernames alice</code></code></pre><h2><strong>The Problem Bloom Cannot Solve: Deletion</strong></h2><p>Bits are shared. Clearing the bits of one element drags down other elements with it. A standard Bloom filter supports insert and query, nothing else.</p><p>The usual patch is a counting Bloom filter: each position holds a counter instead of a single bit, and delete decrements the counter. The cost is memory, about four times the standard filter with 4-bit counters. Counters saturate at 15, so heavy delete traffic still produces errors. It is a patch, and the underlying design still has no native delete.</p><p>For username availability this does not matter: accounts are almost never deregistered, and the set only grows. Blacklists and device lists are different, deletion is a hard requirement.</p><h2><strong>Step Two: Cuckoo Filter When Deletion Matters</strong></h2><p>The Cuckoo filter comes from Fan et al.&#8217;s 2014 paper <em>Cuckoo Filter: Practically Better Than Bloom</em>, built to close exactly this gap.</p><p>Instead of a bit array, it stores a short fingerprint per element. Each element maps to two candidate buckets, and lookups only check those two. When both are full, the new fingerprint kicks out an old one, and the displaced fingerprint moves to its alternate bucket. The name comes from the cuckoo&#8217;s habit of pushing other eggs out of the nest.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!nGae!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13fb499b-7647-45a7-a34d-228f389b7b92_1590x1333.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!nGae!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13fb499b-7647-45a7-a34d-228f389b7b92_1590x1333.png 424w, https://substackcdn.com/image/fetch/$s_!nGae!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13fb499b-7647-45a7-a34d-228f389b7b92_1590x1333.png 848w, https://substackcdn.com/image/fetch/$s_!nGae!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13fb499b-7647-45a7-a34d-228f389b7b92_1590x1333.png 1272w, https://substackcdn.com/image/fetch/$s_!nGae!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13fb499b-7647-45a7-a34d-228f389b7b92_1590x1333.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!nGae!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13fb499b-7647-45a7-a34d-228f389b7b92_1590x1333.png" width="1456" height="1221" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/13fb499b-7647-45a7-a34d-228f389b7b92_1590x1333.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1221,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:214343,&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://seabreeze0828.substack.com/i/210301838?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13fb499b-7647-45a7-a34d-228f389b7b92_1590x1333.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_!nGae!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13fb499b-7647-45a7-a34d-228f389b7b92_1590x1333.png 424w, https://substackcdn.com/image/fetch/$s_!nGae!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13fb499b-7647-45a7-a34d-228f389b7b92_1590x1333.png 848w, https://substackcdn.com/image/fetch/$s_!nGae!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13fb499b-7647-45a7-a34d-228f389b7b92_1590x1333.png 1272w, https://substackcdn.com/image/fetch/$s_!nGae!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F13fb499b-7647-45a7-a34d-228f389b7b92_1590x1333.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p>Kicks push the load factor to about 95%, which keeps the filter compact. Fan et al. compared it directly with the alternatives: against counting Bloom filters it saves about half the space, against a non-deletable, space-optimized Bloom filter it costs 1.5 to 2 times more. The lower the target false positive rate, the smaller the gap. A USENIX ;login article gives a concrete pair of numbers: a Cuckoo filter with 12-bit fingerprints uses about 12.53 bits per element at a 0.19% false positive rate, while a Bloom filter uses 13 bits at 0.20%. At the one-billion scale the two filters sit in the same memory class, the real difference is deletion.</p><p>Fingerprint size follows a simple rule: for a target false positive rate r and bucket size b, take f &gt;= log2(2b/r) bits. With 4-slot buckets and a 1% target, that means 10-bit fingerprints, about 10.5 bits per element including the load factor. Slightly above the Bloom filter&#8217;s 9.6 bits. When deletion is a hard requirement, that extra cost is worth paying.</p><h3><strong>A Simplified Java Implementation with Delete</strong></h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;c6af8855-d5ca-4c1a-8272-15d2f80b442d&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">public class SimpleCuckooFilter {
    private static final int BUCKET_SIZE = 4;    // fingerprints per bucket
    private static final int MAX_KICKS = 500;    // max kick attempts
    private final long[][] buckets;
    private final int numBuckets;

    public SimpleCuckooFilter(int numBuckets) {
        this.buckets = new long[numBuckets][BUCKET_SIZE];
        this.numBuckets = numBuckets;
    }

    private long fingerprint(String value) {
        return value.hashCode() &amp; 0xFFFFL;       // 16-bit fingerprint
    }

    private int index1(String value) {
        return Math.floorMod(value.hashCode(), numBuckets);
    }

    private int index2(int i1, long fp) {
        return Math.floorMod(i1 ^ Long.hashCode(fp), numBuckets);
    }

    private boolean insertInto(int index, long fp) {
        for (int j = 0; j &lt; BUCKET_SIZE; j++) {
            if (buckets[index][j] == 0) {
                buckets[index][j] = fp;
                return true;
            }
        }
        return false;
    }

    public boolean insert(String value) {
        long fp = fingerprint(value);
        int i1 = index1(value);
        int i2 = index2(i1, fp);
        if (insertInto(i1, fp) || insertInto(i2, fp)) {
            return true;
        }

        int index = (Math.random() &lt; 0.5) ? i1 : i2;
        for (int k = 0; k &lt; MAX_KICKS; k++) {
            int slot = (int) (Math.random() * BUCKET_SIZE);
            long kicked = buckets[index][slot];
            buckets[index][slot] = fp;
            fp = kicked;
            index = index2(index, fp);
            if (insertInto(index, fp)) {
                return true;
            }
        }
        return false;                            // too full, expand or rebuild
    }

    public boolean contains(String value) {
        long fp = fingerprint(value);
        int i1 = index1(value);
        int i2 = index2(i1, fp);
        for (int j = 0; j &lt; BUCKET_SIZE; j++) {
            if (buckets[i1][j] == fp || buckets[i2][j] == fp) {
                return true;
            }
        }
        return false;
    }

    public boolean delete(String value) {
        long fp = fingerprint(value);
        int i1 = index1(value);
        int i2 = index2(i1, fp);
        for (int j = 0; j &lt; BUCKET_SIZE; j++) {
            if (buckets[i1][j] == fp) {
                buckets[i1][j] = 0;
                return true;
            }
        }
        for (int j = 0; j &lt; BUCKET_SIZE; j++) {
            if (buckets[i2][j] == fp) {
                buckets[i2][j] = 0;
                return true;
            }
        }
        return false;
    }
}
</code></pre></div><p>This is a teaching version: empty slots are marked with 0 and fingerprints are only 16 bits. A production implementation also has to handle fingerprint collisions, delete edge cases, and expansion. One warning: deleting a fingerprint that was never inserted, a false positive, removes someone else&#8217;s fingerprint and creates a false negative. Only delete what you actually added.</p><p>Redis provides the same operations, and delete is the one command Bloom cannot offer.</p><pre><code><code>CF.RESERVE usernames 1000000000 BUCKETSIZE 4
CF.ADD usernames alice
CF.EXISTS usernames alice
CF.DEL usernames alice
</code></code></pre><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://seabreeze0828.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">Java, SpringBoot &amp; AI Development: Coding Skills &amp; Interview is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2><strong>Rewriting the Original Example: A Filter-First Service</strong></h2><p>The previous article cached query results with short TTLs. This version moves the filter in front of the database, which stays the source of truth.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;37520a32-ecdd-495b-85aa-ccadeeed7b4e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">@Service
public class UsernameAvailabilityService {

    private final BloomFilter&lt;String&gt; takenUsernames = BloomFilter.create(
            Funnels.stringFunnel(StandardCharsets.UTF_8),
            1_000_000_000L,
            0.01);

    private final UserRepository userRepository;

    public UsernameAvailabilityService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public boolean isAvailable(String username) {
        if (!takenUsernames.mightContain(username)) {
            return true; // definitely absent, no database call
        }
        // filter says "possibly taken", verify against the source of truth
        return !userRepository.existsByUsername(username);
    }

    @Transactional
    public void register(String username) {
        if (!isAvailable(username)) {
            throw new UsernameTakenException(username);
        }
        try {
            userRepository.insert(username); // unique index is the final gate
            takenUsernames.put(username);    // keep the filter warm
        } catch (DuplicateKeyException e) {
            throw new UsernameTakenException(username);
        }
    }
}
</code></pre></div><p>The flow matches the original architecture diagram: the filter absorbs the vast majority of requests, and the database only sees the &#8220;possibly taken&#8221; cases plus the final insert.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!fEqw!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1cfbb282-886b-4aa6-b654-462ed15077c6_1590x1041.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!fEqw!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1cfbb282-886b-4aa6-b654-462ed15077c6_1590x1041.png 424w, https://substackcdn.com/image/fetch/$s_!fEqw!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1cfbb282-886b-4aa6-b654-462ed15077c6_1590x1041.png 848w, https://substackcdn.com/image/fetch/$s_!fEqw!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1cfbb282-886b-4aa6-b654-462ed15077c6_1590x1041.png 1272w, https://substackcdn.com/image/fetch/$s_!fEqw!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1cfbb282-886b-4aa6-b654-462ed15077c6_1590x1041.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!fEqw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1cfbb282-886b-4aa6-b654-462ed15077c6_1590x1041.png" width="1456" height="953" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1cfbb282-886b-4aa6-b654-462ed15077c6_1590x1041.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:953,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:181261,&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://seabreeze0828.substack.com/i/210301838?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1cfbb282-886b-4aa6-b654-462ed15077c6_1590x1041.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_!fEqw!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1cfbb282-886b-4aa6-b654-462ed15077c6_1590x1041.png 424w, https://substackcdn.com/image/fetch/$s_!fEqw!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1cfbb282-886b-4aa6-b654-462ed15077c6_1590x1041.png 848w, https://substackcdn.com/image/fetch/$s_!fEqw!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1cfbb282-886b-4aa6-b654-462ed15077c6_1590x1041.png 1272w, https://substackcdn.com/image/fetch/$s_!fEqw!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1cfbb282-886b-4aa6-b654-462ed15077c6_1590x1041.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><p>Lists that need deletion use the same pattern, just with a Cuckoo filter. The service below keeps a blacklist in memory and supports unbanning, something the Bloom version cannot do without a rebuild.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;f85d5285-1d71-4189-bf25-a22abb9b7043&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">public class BlacklistService {
    private final SimpleCuckooFilter blockedTokens = new SimpleCuckooFilter(1 &lt;&lt; 20);

    public boolean isBlocked(String token) {
        return blockedTokens.contains(token);
    }

    public void block(String token) {
        blockedTokens.insert(token);
    }

    public void unblock(String token) {
        blockedTokens.delete(token);
    }
}
</code></pre></div><h2><strong>Handling Concurrency</strong></h2><p>The filter answers the membership question, it is not a transaction. Two users can both see the same username as &#8220;available&#8221;, and the unique index turns the race into a single winner.</p><p>The cleanest approach is atomic check-and-insert: try the insert, let the database enforce the constraint, and translate <code>DuplicateKeyException</code> into a friendly error. The optimistic and pessimistic locking from the previous article still applies to rows with extra state. For the username itself, the unique index is simpler and faster than any application-level lock.</p><p>There is a second concurrency concern the previous article never had to face: not every Bloom filter implementation is thread-safe for concurrent writes. If the filter lives in application memory, writes need synchronization or a thread-safe variant. If it lives in Redis, commands execute atomically on the server, one more reason to take the Redis path.</p><h2><strong>Performance Considerations</strong></h2><p>You can shard the database by user id or username hash. The filter stays whole, it only answers existence, and 1.2 GB is small enough for every application node to keep a copy.</p><p>The Bloom filter has no delete, so a shrinking list eventually needs a rebuild. A rebuild is not a config change: you replay the current set, load the new filter, switch traffic, and watch the false positive rate for a while.</p><p>When insert still fails after the maximum number of kicks, the Cuckoo filter is full. Redis&#8217;s <code>CF.RESERVE</code> supports expansion parameters, and production implementations grow the table instead of failing the request.</p><p>The availability check must be synchronous. Downstream work like confirmation emails or analytics can go into a queue.</p><h2><strong>Which Filter Do You Pick</strong></h2><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!fQP8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5e05253-70d9-4a07-97f6-c47662da66af_2055x1210.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!fQP8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5e05253-70d9-4a07-97f6-c47662da66af_2055x1210.png 424w, https://substackcdn.com/image/fetch/$s_!fQP8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5e05253-70d9-4a07-97f6-c47662da66af_2055x1210.png 848w, https://substackcdn.com/image/fetch/$s_!fQP8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5e05253-70d9-4a07-97f6-c47662da66af_2055x1210.png 1272w, https://substackcdn.com/image/fetch/$s_!fQP8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5e05253-70d9-4a07-97f6-c47662da66af_2055x1210.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!fQP8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5e05253-70d9-4a07-97f6-c47662da66af_2055x1210.png" width="1456" height="857" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c5e05253-70d9-4a07-97f6-c47662da66af_2055x1210.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:857,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:232619,&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://seabreeze0828.substack.com/i/210301838?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5e05253-70d9-4a07-97f6-c47662da66af_2055x1210.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_!fQP8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5e05253-70d9-4a07-97f6-c47662da66af_2055x1210.png 424w, https://substackcdn.com/image/fetch/$s_!fQP8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5e05253-70d9-4a07-97f6-c47662da66af_2055x1210.png 848w, https://substackcdn.com/image/fetch/$s_!fQP8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5e05253-70d9-4a07-97f6-c47662da66af_2055x1210.png 1272w, https://substackcdn.com/image/fetch/$s_!fQP8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc5e05253-70d9-4a07-97f6-c47662da66af_2055x1210.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Answer one question first: will the list delete entries?</p><p>No, use a Bloom filter. At typical false positive rates it is simpler and smaller, with a mature ecosystem: Guava and Redis ship it, most databases do too. One-billion-username checks are its standard use case.</p><p>Yes, then look at how often and how much. If deletions are sparse and the list can be rebuilt, stay with Bloom plus periodic rebuilds. With frequent deletions at scale, the rebuild cost becomes unbearable, use the Cuckoo filter there.</p><p>DimensionBloom FilterCuckoo FilterBits per element, 1% FPR~9.6~10.5Bits per element, ~0.2% FPR~13~12.5DeletionnoyesInsert can failnoyes, needs expansionLoad factorn/a~95%Java ecosystemGuava, matureno Guava-level defaultRedis supportBF.* commandsCF.* commands</p><p>The Cuckoo filter costs more to implement and adds an insert-failure path. That cost is only justified when deletion is a real requirement. Blacklists and device lists qualify. Username availability, where accounts almost never disappear, does not.</p><h2><strong>References</strong></h2><ul><li><p>Fan et al., <em>Cuckoo Filter: Practically Better Than Bloom</em>, ACM CoNEXT 2014</p></li><li><p>USENIX ;login article with the 12.53 vs 13 bits comparison, <a href="https://www.usenix.org/system/files/login/issues/1308_login_online.pdf">https://www.usenix.org/system/files/login/issues/1308_login_online.pdf</a></p></li><li><p>ScyllaDB glossary entry on Bloom filters, 9.6 bits per element at 1% FPR, <a href="https://www.scylladb.com/glossary/bloom-filter/">https://www.scylladb.com/glossary/bloom-filter/</a></p></li><li><p>Redis Stack documentation for the CF.ADD command, <a href="https://redis-stack.io/commands/cf.add/">https://redis-stack.io/commands/cf.add/</a></p></li><li><p>Guava BloomFilter API documentation, <a href="https://guava.dev/releases/31.0-jre/api/docs/com/google/common/hash/BloomFilter.html">https://guava.dev/releases/31.0-jre/api/docs/com/google/common/hash/BloomFilter.html</a></p></li></ul><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://seabreeze0828.substack.com/?utm_source=substack&amp;utm_medium=email&amp;utm_content=share&amp;action=share&quot;,&quot;text&quot;:&quot;Share Java, SpringBoot &amp; AI Development: Coding Skills &amp; Interview&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://seabreeze0828.substack.com/?utm_source=substack&amp;utm_medium=email&amp;utm_content=share&amp;action=share"><span>Share Java, SpringBoot &amp; AI Development: Coding Skills &amp; Interview</span></a></p><p></p>]]></content:encoded></item><item><title><![CDATA[Interview: How to Check Whether a Username Exists Among One Billion Users?]]></title><description><![CDATA[The space&#8209;efficient Bloom filter swiftly checks username occupancy. Its minor false&#8209;positive flaw can be handled via auxiliary database verification.]]></description><link>https://seabreeze0828.substack.com/p/interview-how-to-check-whether-a</link><guid isPermaLink="false">https://seabreeze0828.substack.com/p/interview-how-to-check-whether-a</guid><dc:creator><![CDATA[Dylan Smith]]></dc:creator><pubDate>Fri, 07 Aug 2026 18:08:22 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!5o5b!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F31128a96-92b3-486c-a6ca-d26aa7dacb9e_1313x859.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!5o5b!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F31128a96-92b3-486c-a6ca-d26aa7dacb9e_1313x859.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!5o5b!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F31128a96-92b3-486c-a6ca-d26aa7dacb9e_1313x859.png 424w, https://substackcdn.com/image/fetch/$s_!5o5b!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F31128a96-92b3-486c-a6ca-d26aa7dacb9e_1313x859.png 848w, https://substackcdn.com/image/fetch/$s_!5o5b!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F31128a96-92b3-486c-a6ca-d26aa7dacb9e_1313x859.png 1272w, https://substackcdn.com/image/fetch/$s_!5o5b!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F31128a96-92b3-486c-a6ca-d26aa7dacb9e_1313x859.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!5o5b!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F31128a96-92b3-486c-a6ca-d26aa7dacb9e_1313x859.png" width="1313" height="859" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/31128a96-92b3-486c-a6ca-d26aa7dacb9e_1313x859.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:859,&quot;width&quot;:1313,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!5o5b!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F31128a96-92b3-486c-a6ca-d26aa7dacb9e_1313x859.png 424w, https://substackcdn.com/image/fetch/$s_!5o5b!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F31128a96-92b3-486c-a6ca-d26aa7dacb9e_1313x859.png 848w, https://substackcdn.com/image/fetch/$s_!5o5b!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F31128a96-92b3-486c-a6ca-d26aa7dacb9e_1313x859.png 1272w, https://substackcdn.com/image/fetch/$s_!5o5b!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F31128a96-92b3-486c-a6ca-d26aa7dacb9e_1313x859.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><strong>Preface</strong></h2><p>I wonder if everyone has noticed that when registering with some apps, it prompts you that the username has been occupied and you need to change one.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://seabreeze0828.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://seabreeze0828.substack.com/subscribe?"><span>Subscribe now</span></a></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!4TgV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f69fa1a-9f7a-477e-846c-9181b67feb73_1160x1272.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!4TgV!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f69fa1a-9f7a-477e-846c-9181b67feb73_1160x1272.png 424w, https://substackcdn.com/image/fetch/$s_!4TgV!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f69fa1a-9f7a-477e-846c-9181b67feb73_1160x1272.png 848w, https://substackcdn.com/image/fetch/$s_!4TgV!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f69fa1a-9f7a-477e-846c-9181b67feb73_1160x1272.png 1272w, https://substackcdn.com/image/fetch/$s_!4TgV!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f69fa1a-9f7a-477e-846c-9181b67feb73_1160x1272.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!4TgV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f69fa1a-9f7a-477e-846c-9181b67feb73_1160x1272.png" width="1160" height="1272" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8f69fa1a-9f7a-477e-846c-9181b67feb73_1160x1272.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1272,&quot;width&quot;:1160,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!4TgV!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f69fa1a-9f7a-477e-846c-9181b67feb73_1160x1272.png 424w, https://substackcdn.com/image/fetch/$s_!4TgV!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f69fa1a-9f7a-477e-846c-9181b67feb73_1160x1272.png 848w, https://substackcdn.com/image/fetch/$s_!4TgV!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f69fa1a-9f7a-477e-846c-9181b67feb73_1160x1272.png 1272w, https://substackcdn.com/image/fetch/$s_!4TgV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8f69fa1a-9f7a-477e-846c-9181b67feb73_1160x1272.png 1456w" sizes="100vw"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>There are many ways to implement this function. Now let&#8217;s take a look at the advantages and disadvantages of different design schemes one by one.</p><h2><strong>Database Scheme</strong></h2><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!XCSD!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3dfa7616-ee43-4573-a5c7-c258a9db5ee4_1313x159.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!XCSD!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3dfa7616-ee43-4573-a5c7-c258a9db5ee4_1313x159.png 424w, https://substackcdn.com/image/fetch/$s_!XCSD!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3dfa7616-ee43-4573-a5c7-c258a9db5ee4_1313x159.png 848w, https://substackcdn.com/image/fetch/$s_!XCSD!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3dfa7616-ee43-4573-a5c7-c258a9db5ee4_1313x159.png 1272w, https://substackcdn.com/image/fetch/$s_!XCSD!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3dfa7616-ee43-4573-a5c7-c258a9db5ee4_1313x159.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!XCSD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3dfa7616-ee43-4573-a5c7-c258a9db5ee4_1313x159.png" width="1313" height="159" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3dfa7616-ee43-4573-a5c7-c258a9db5ee4_1313x159.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:159,&quot;width&quot;:1313,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!XCSD!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3dfa7616-ee43-4573-a5c7-c258a9db5ee4_1313x159.png 424w, https://substackcdn.com/image/fetch/$s_!XCSD!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3dfa7616-ee43-4573-a5c7-c258a9db5ee4_1313x159.png 848w, https://substackcdn.com/image/fetch/$s_!XCSD!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3dfa7616-ee43-4573-a5c7-c258a9db5ee4_1313x159.png 1272w, https://substackcdn.com/image/fetch/$s_!XCSD!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3dfa7616-ee43-4573-a5c7-c258a9db5ee4_1313x159.png 1456w" sizes="100vw"></picture><div></div></div></a></figure></div><p>This method is the simplest to implement, but it will bring the following problems:</p><ol><li><p>There are performance problems with relatively high latency. If the data volume is huge, the query speed will become slow. Moreover, database queries involve network communication between the application server and the database server. The time required for establishing connections, sending queries, and receiving responses also causes latency.</p></li><li><p>The database load is excessive. Frequent execution of SELECT queries to check the uniqueness of usernames, and each query consumes database resources, including CPU and I/O resources.</p></li><li><p>Poor scalability. Databases have limitations on concurrent connections and resources. If the registration rate continues to rise, the database server may have difficulty handling the increasing number of incoming requests. Vertical expansion of the database (adding more resources to a single server) may be costly and may have limitations.</p></li></ol><h2><strong>Cache Solution</strong></h2><p>To address the performance issue of database calls for checking the uniqueness of usernames, an efficient Redis cache is introduced.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!dTyz!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9828ea4b-eca0-4a67-8957-2b0a805a644f_1313x201.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!dTyz!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9828ea4b-eca0-4a67-8957-2b0a805a644f_1313x201.png 424w, https://substackcdn.com/image/fetch/$s_!dTyz!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9828ea4b-eca0-4a67-8957-2b0a805a644f_1313x201.png 848w, https://substackcdn.com/image/fetch/$s_!dTyz!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9828ea4b-eca0-4a67-8957-2b0a805a644f_1313x201.png 1272w, https://substackcdn.com/image/fetch/$s_!dTyz!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9828ea4b-eca0-4a67-8957-2b0a805a644f_1313x201.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!dTyz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9828ea4b-eca0-4a67-8957-2b0a805a644f_1313x201.png" width="1313" height="201" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9828ea4b-eca0-4a67-8957-2b0a805a644f_1313x201.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:201,&quot;width&quot;:1313,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!dTyz!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9828ea4b-eca0-4a67-8957-2b0a805a644f_1313x201.png 424w, https://substackcdn.com/image/fetch/$s_!dTyz!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9828ea4b-eca0-4a67-8957-2b0a805a644f_1313x201.png 848w, https://substackcdn.com/image/fetch/$s_!dTyz!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9828ea4b-eca0-4a67-8957-2b0a805a644f_1313x201.png 1272w, https://substackcdn.com/image/fetch/$s_!dTyz!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9828ea4b-eca0-4a67-8957-2b0a805a644f_1313x201.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;e55ec681-d714-45af-9be5-027966935fd5&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">import org.redisson.Redisson;  import org.redisson.api.RedissonClient;  import org.redisson.config.Config;  import org.redisson.api.RMap;  
  public class UserExistenceChecker {  
  
    // Redis hash map name to store user information  
    private static final String USER_HASH_NAME = &#8220;users&#8221;;  
  
    public static void main(String[] args) {  
        // Create a Redisson client  
        RedissonClient redisson = createRedissonClient();  
  
        // Retrieve the hash map to store user information  
        RMap&lt;String, String&gt; users = redisson.getMap(USER_HASH_NAME);  
  
        // Add a user to the hash map  
        users.put(&#8221;user123&#8221;, &#8220;someUserInfo&#8221;); // Here &#8220;someUserInfo&#8221; could be a JSON string, UUID, etc.  
  
        // Check if a user exists  
        boolean exists = users.containsKey(&#8221;user123&#8221;);  
        System.out.println(&#8221;User &#8216;user123&#8217; exists? &#8220; + exists);  
  
        // Check for a non-existent user  
        exists = users.containsKey(&#8221;user456&#8221;);  
        System.out.println(&#8221;User &#8216;user456&#8217; exists? &#8220; + exists);  
  
        // Shutdown the Redisson client  
        redisson.shutdown();  
    }  
  
    // Helper method to create a Redisson client  
    private static RedissonClient createRedissonClient() {  
        Config config = new Config();  
        config.useSingleServer()  
                .setAddress(&#8221;redis://127.0.0.1:6379&#8221;) // Adjust to your Redis address  
                .setPassword(&#8221;yourpassword&#8221;); // Provide your Redis password if any  
  
        return Redisson.create(config);  
    }  
}</code></pre></div><p>The biggest problem with this solution is the excessive memory usage. Suppose each username requires approximately 15 bytes of memory. If you want to store one billion usernames, you would need 15GB of memory.</p><blockquote><p><em>Total memory = Memory usage per record * Number of records = 15 bytes/record * 1,000,000,000 records = 15,000,000,000 bytes &#8776; 15,000,000 KB &#8776; 15,000 MB &#8776; 15 GB</em></p></blockquote><h2><strong>Bloom Filter Scheme</strong></h2><p>If direct caching for judgment results in excessive memory usage, is there a better way? The Bloom Filter is a very good choice.</p><h3><strong>What is a Bloom Filter?</strong></h3><p>The Bloom Filter is a highly space-efficient random data structure. It uses a bit array to concisely represent a set and can determine whether an element belongs to this set.</p><p>This efficiency of the Bloom Filter comes at a certain cost: When determining whether an element belongs to a certain set, it is possible to mistakenly consider an element that does not belong to the set as belonging to the set (false positive).</p><p>Therefore, the Bloom Filter is not suitable for application scenarios with &#8220;zero errors&#8221;. However, in application scenarios that can tolerate a low error rate, the Bloom Filter achieves significant savings in storage space through very few errors.</p><h3><strong>Structure</strong></h3><p>It can be known from the above analysis that the core idea of the Bloom filter is to use a bit array (<code>bit array</code>) and a set of hash functions.</p><p>The bit array, each bit is 0 initially</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ib1q!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe6a2090f-1580-46a6-8c49-f9ee922630c7_294x47.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ib1q!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe6a2090f-1580-46a6-8c49-f9ee922630c7_294x47.png 424w, https://substackcdn.com/image/fetch/$s_!ib1q!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe6a2090f-1580-46a6-8c49-f9ee922630c7_294x47.png 848w, https://substackcdn.com/image/fetch/$s_!ib1q!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe6a2090f-1580-46a6-8c49-f9ee922630c7_294x47.png 1272w, https://substackcdn.com/image/fetch/$s_!ib1q!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe6a2090f-1580-46a6-8c49-f9ee922630c7_294x47.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ib1q!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe6a2090f-1580-46a6-8c49-f9ee922630c7_294x47.png" width="294" height="47" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e6a2090f-1580-46a6-8c49-f9ee922630c7_294x47.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:47,&quot;width&quot;:294,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!ib1q!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe6a2090f-1580-46a6-8c49-f9ee922630c7_294x47.png 424w, https://substackcdn.com/image/fetch/$s_!ib1q!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe6a2090f-1580-46a6-8c49-f9ee922630c7_294x47.png 848w, https://substackcdn.com/image/fetch/$s_!ib1q!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe6a2090f-1580-46a6-8c49-f9ee922630c7_294x47.png 1272w, https://substackcdn.com/image/fetch/$s_!ib1q!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe6a2090f-1580-46a6-8c49-f9ee922630c7_294x47.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>When inserting the value x, use k hash functions (3 in the figure) to hash the value of x respectively, and take the remainder of the hash value and the capacity(bit array length) of the Bloom filter, and set the value of the corresponding bit represented by the result to 1.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!a61j!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F457dc6d7-442a-45e0-b568-76d9466a72fa_294x71.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!a61j!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F457dc6d7-442a-45e0-b568-76d9466a72fa_294x71.png 424w, https://substackcdn.com/image/fetch/$s_!a61j!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F457dc6d7-442a-45e0-b568-76d9466a72fa_294x71.png 848w, https://substackcdn.com/image/fetch/$s_!a61j!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F457dc6d7-442a-45e0-b568-76d9466a72fa_294x71.png 1272w, https://substackcdn.com/image/fetch/$s_!a61j!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F457dc6d7-442a-45e0-b568-76d9466a72fa_294x71.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!a61j!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F457dc6d7-442a-45e0-b568-76d9466a72fa_294x71.png" width="294" height="71" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/457dc6d7-442a-45e0-b568-76d9466a72fa_294x71.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:71,&quot;width&quot;:294,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!a61j!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F457dc6d7-442a-45e0-b568-76d9466a72fa_294x71.png 424w, https://substackcdn.com/image/fetch/$s_!a61j!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F457dc6d7-442a-45e0-b568-76d9466a72fa_294x71.png 848w, https://substackcdn.com/image/fetch/$s_!a61j!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F457dc6d7-442a-45e0-b568-76d9466a72fa_294x71.png 1272w, https://substackcdn.com/image/fetch/$s_!a61j!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F457dc6d7-442a-45e0-b568-76d9466a72fa_294x71.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>A search process is similar to an insertion process. Similarly, k hash functions are used to hash the value to be searched. Only when the value of each bit obtained by hashing is 1 does it indicate that the value &#8220;possibly&#8221; truly exists; conversely, if the value of any bit is 0, it indicates that the value must not exist. For example, y1 must not exist; while y2 may exist.</p><div class="captioned-image-container"><figure><a class="image-link image2" target="_blank" href="https://substackcdn.com/image/fetch/$s_!21X1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f0d8a95-df2f-4005-b5d0-70514c5522da_296x67.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!21X1!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f0d8a95-df2f-4005-b5d0-70514c5522da_296x67.png 424w, https://substackcdn.com/image/fetch/$s_!21X1!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f0d8a95-df2f-4005-b5d0-70514c5522da_296x67.png 848w, https://substackcdn.com/image/fetch/$s_!21X1!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f0d8a95-df2f-4005-b5d0-70514c5522da_296x67.png 1272w, https://substackcdn.com/image/fetch/$s_!21X1!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f0d8a95-df2f-4005-b5d0-70514c5522da_296x67.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!21X1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f0d8a95-df2f-4005-b5d0-70514c5522da_296x67.png" width="296" height="67" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9f0d8a95-df2f-4005-b5d0-70514c5522da_296x67.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:67,&quot;width&quot;:296,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!21X1!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f0d8a95-df2f-4005-b5d0-70514c5522da_296x67.png 424w, https://substackcdn.com/image/fetch/$s_!21X1!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f0d8a95-df2f-4005-b5d0-70514c5522da_296x67.png 848w, https://substackcdn.com/image/fetch/$s_!21X1!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f0d8a95-df2f-4005-b5d0-70514c5522da_296x67.png 1272w, https://substackcdn.com/image/fetch/$s_!21X1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9f0d8a95-df2f-4005-b5d0-70514c5522da_296x67.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></a></figure></div><p>Redis itself supports the data structure of the Bloom filter. Let&#8217;s simply implement the code using the Redisson client:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;699cd7f5-b8d1-4c07-bfb3-403391058c42&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">import org.redisson.Redisson;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;

public class UserExistenceChecker {

    // Name of the Bloom Filter in Redis
    private static final String BLOOM_FILTER_NAME = &#8220;user_existence_filter&#8221;;

    public static void main(String[] args) {
        // Create a Redisson client
        RedissonClient redisson = createRedissonClient();

        // Retrieve or create a Bloom Filter instance
        // Expected number of elements and false positive probability are parameters
        RBloomFilter&lt;String&gt; bloomFilter = redisson.getBloomFilter(BLOOM_FILTER_NAME);
        bloomFilter.tryInit(100000L, 0.001); // Initialize the Bloom Filter with expected elements and false positive rate

        // Add a user to the Bloom Filter
        bloomFilter.add(&#8221;user123&#8221;);

        // Check if a user exists
        boolean exists = bloomFilter.contains(&#8221;user123&#8221;); // Should return true
        System.out.println(&#8221;User &#8216;user123&#8217; exists? &#8220; + exists);

        // Check for a non-existent user (might falsely report as true due to Bloom Filter&#8217;s nature)
        exists = bloomFilter.contains(&#8221;user456&#8221;); // Assuming not added, should ideally return false, but could be a false positive
        System.out.println(&#8221;User &#8216;user456&#8217; exists? &#8220; + exists);

        // Shutdown the Redisson client
        redisson.shutdown();
    }

    // Helper method to create a Redisson client
    private static RedissonClient createRedissonClient() {
        Config config = new Config();
        config.useSingleServer()
                .setAddress(&#8221;redis://127.0.0.1:6379&#8221;); // Adjust to your Redis address
//                .setPassword(&#8221;yourpassword&#8221;); // Provide your Redis password if any

        return Redisson.create(config);
    }
}</code></pre></div><p><em><strong>Advantages:</strong></em></p><ul><li><p><strong>Memory space saving:</strong> Compared to using data structures such as hash tables, the Bloom filter usually requires less memory space because it does not store actual elements but only the hash values of the elements. If storing 1 billion records with an error <code>probability</code> of 0.001, only <code>1.67 GB</code> of memory is needed. Compared to the original <code>15G</code>, it has been greatly reduced.</p></li><li><p><strong>Efficient lookup:</strong> The Bloom filter can quickly determine whether an element exists in the set in constant time <code>(O(1))</code>, without traversing the entire set.</p></li></ul><p><em><strong>Disadvantages</strong></em>:</p><ul><li><p><strong>False positive rate exists:</strong> When the Bloom filter determines whether an element exists, there is a certain false positive rate. This means that in some cases, it may wrongly report that an element exists, but will not wrongly report that an element does not exist.<strong>However, this generally has little impact.</strong></p></li><li><p><strong>Cannot delete elements</strong>: The Bloom filter usually does not support deleting elements from the set because deleting an element will affect the hash values of other elements, increasing the false positive rate.</p></li></ul><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://seabreeze0828.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">Java, SpringBoot &amp; AI Development: Coding Skills &amp; Interview is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h3><strong>How to ensure that the Bloom filter scheme has no false positives?</strong></h3><p>Here, the scheme of combining the Bloom filter with the database can be considered.</p><p>When the Bloom filter is used to determine whether an element exists, there is a certain probability that it may wrongly report that the element exists, but it will not wrongly report that the element does not exist.</p><p>So, when using the Bloom filter to determine that an element does not exist, you can directly trust this result and return. If it is determined that an element exists, don&#8217;t completely trust its judgment at this time. Instead, query the real result in the database.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_SL8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe024c14c-ca76-4c27-9dbc-e142b4ab3109_1313x522.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_SL8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe024c14c-ca76-4c27-9dbc-e142b4ab3109_1313x522.png 424w, https://substackcdn.com/image/fetch/$s_!_SL8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe024c14c-ca76-4c27-9dbc-e142b4ab3109_1313x522.png 848w, https://substackcdn.com/image/fetch/$s_!_SL8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe024c14c-ca76-4c27-9dbc-e142b4ab3109_1313x522.png 1272w, https://substackcdn.com/image/fetch/$s_!_SL8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe024c14c-ca76-4c27-9dbc-e142b4ab3109_1313x522.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_SL8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe024c14c-ca76-4c27-9dbc-e142b4ab3109_1313x522.png" width="1313" height="522" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e024c14c-ca76-4c27-9dbc-e142b4ab3109_1313x522.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:522,&quot;width&quot;:1313,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!_SL8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe024c14c-ca76-4c27-9dbc-e142b4ab3109_1313x522.png 424w, https://substackcdn.com/image/fetch/$s_!_SL8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe024c14c-ca76-4c27-9dbc-e142b4ab3109_1313x522.png 848w, https://substackcdn.com/image/fetch/$s_!_SL8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe024c14c-ca76-4c27-9dbc-e142b4ab3109_1313x522.png 1272w, https://substackcdn.com/image/fetch/$s_!_SL8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe024c14c-ca76-4c27-9dbc-e142b4ab3109_1313x522.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Because the probability of determining that an element exists is already very low, the actual magnitude of accessing the database will also be very small, and the overall pressure is not great.</p><h3><strong>Can elements be deleted from a Bloom filter?</strong></h3><p>Why can&#8217;t elements be deleted from a Bloom Filter? We can use an example to illustrate.</p><p>For example, if we want to delete the member &#8220;Jerry&#8221; from the set, then we will first use k(2 in the figure) hash functions to calculate it. Since &#8220;Jerry&#8221; is already a member of the set, the corresponding positions in the bit array must be 1. If we want to delete this member &#8220;Jerry&#8221;, we need to set all the 1s at the calculated positions to 0. In the following figure, just set the values at index positions 2 and 5 to 0.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!i5oX!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf656568-7978-45db-97e4-e675d3684c71_1118x569.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!i5oX!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf656568-7978-45db-97e4-e675d3684c71_1118x569.png 424w, https://substackcdn.com/image/fetch/$s_!i5oX!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf656568-7978-45db-97e4-e675d3684c71_1118x569.png 848w, https://substackcdn.com/image/fetch/$s_!i5oX!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf656568-7978-45db-97e4-e675d3684c71_1118x569.png 1272w, https://substackcdn.com/image/fetch/$s_!i5oX!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf656568-7978-45db-97e4-e675d3684c71_1118x569.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!i5oX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf656568-7978-45db-97e4-e675d3684c71_1118x569.png" width="1118" height="569" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/af656568-7978-45db-97e4-e675d3684c71_1118x569.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:569,&quot;width&quot;:1118,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!i5oX!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf656568-7978-45db-97e4-e675d3684c71_1118x569.png 424w, https://substackcdn.com/image/fetch/$s_!i5oX!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf656568-7978-45db-97e4-e675d3684c71_1118x569.png 848w, https://substackcdn.com/image/fetch/$s_!i5oX!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf656568-7978-45db-97e4-e675d3684c71_1118x569.png 1272w, https://substackcdn.com/image/fetch/$s_!i5oX!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faf656568-7978-45db-97e4-e675d3684c71_1118x569.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Here comes the problem! Now, let&#8217;s assume that &#8220;Tom&#8221; is also already an element of the set. If we need to query whether &#8220;Tom&#8221; is in the set, after calculation by the hash function, we will judge whether the third and fifth positions are 1. At this time, we get the result that the fifth position is 0, that is, &#8220;Tom&#8221; does not belong to the set. Obviously, this is a false positive here.</p><p>So, the original Bloom Filter does not support deleting elements, but the Counting Bloom Filter can.</p><p>The emergence of the Counting Bloom Filter solves the above problem. It extends each bit of the bit array of the standard Bloom Filter to a small counter. When inserting an element, the values of the corresponding k (k is the number of hash functions) counters are incremented by 1 respectively. When deleting an element, the values of the corresponding k counters are decremented by 1 respectively.</p><p>From this, we can see that the Counting Bloom Filter adds a deletion operation to the Bloom Filter at the cost of occupying several times more storage space. Isn&#8217;t the basic principle very simple? Looking at the following picture, you can understand where the difference between it and the Bloom Filter lies.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!486Q!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a972e1d-9767-427f-8336-bc66d6eeb5ce_1200x603.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!486Q!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a972e1d-9767-427f-8336-bc66d6eeb5ce_1200x603.png 424w, https://substackcdn.com/image/fetch/$s_!486Q!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a972e1d-9767-427f-8336-bc66d6eeb5ce_1200x603.png 848w, https://substackcdn.com/image/fetch/$s_!486Q!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a972e1d-9767-427f-8336-bc66d6eeb5ce_1200x603.png 1272w, https://substackcdn.com/image/fetch/$s_!486Q!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a972e1d-9767-427f-8336-bc66d6eeb5ce_1200x603.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!486Q!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a972e1d-9767-427f-8336-bc66d6eeb5ce_1200x603.png" width="1200" height="603" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8a972e1d-9767-427f-8336-bc66d6eeb5ce_1200x603.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:603,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!486Q!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a972e1d-9767-427f-8336-bc66d6eeb5ce_1200x603.png 424w, https://substackcdn.com/image/fetch/$s_!486Q!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a972e1d-9767-427f-8336-bc66d6eeb5ce_1200x603.png 848w, https://substackcdn.com/image/fetch/$s_!486Q!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a972e1d-9767-427f-8336-bc66d6eeb5ce_1200x603.png 1272w, https://substackcdn.com/image/fetch/$s_!486Q!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8a972e1d-9767-427f-8336-bc66d6eeb5ce_1200x603.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3><strong>Selection of Counter Size</strong></h3><p>A major difference between the Counting Bloom Filter and the Bloom Filter is that the Counting Bloom Filter replaces one bit in the Bloom Filter with a Counter.</p><p>So, how large should the Counter be? Here, the issue of space utilization needs to be considered. From a usage perspective, of course, the larger the better, because a larger Counter can represent more information. But a larger Counter means more resource occupation and often causes a great waste of space.</p><p>Therefore, when choosing a Counter, we should try to meet the requirements as much as possible. The specific calculation of the Counter is relatively complex and involves a series of mathematical formulas. It will not be discussed here. Interested friends can refer to pages 6 and 7 of the paper: <em>&#8220;Summary Cache: A Scalable Wide-Area Web Cache Sharing Protocol&#8221;, </em>which specifically elaborates on it.</p><h2><strong>Summary</strong></h2><p>The Redis Bloom filter scheme provides an efficient memory-based solution for uniqueness verification under large data volumes. It needs to strike a balance between memory consumption and error rate. Of course, the Bloom filter has more application scenarios, such as preventing cache penetration and preventing malicious access.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://seabreeze0828.substack.com/?utm_source=substack&amp;utm_medium=email&amp;utm_content=share&amp;action=share&quot;,&quot;text&quot;:&quot;Share Java, SpringBoot &amp; AI Development: Coding Skills &amp; Interview&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://seabreeze0828.substack.com/?utm_source=substack&amp;utm_medium=email&amp;utm_content=share&amp;action=share"><span>Share Java, SpringBoot &amp; AI Development: Coding Skills &amp; Interview</span></a></p><p></p><p></p>]]></content:encoded></item></channel></rss>