<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-5008017311510568944</id><updated>2026-10-21T00:15:32.310-07:00</updated><category term="java"/><category term="Distributed systems"/><category term="architecture"/><category term="Databases"/><category term="distributed applications"/><category term="AI"/><category term="AI Agent"/><category term="Agentic AI"/><category term="Artificial Intelligence"/><category term="BigData"/><category term="Kafka Messaging BigData"/><category term="System Design"/><category term="web"/><category term="Cassandra"/><category term="JAVA concurrency scalability"/><category term="LLM"/><category term="Large language Model"/><category term="Spring"/><category term="java performance"/><category term="Cache"/><category term="CockRoachDB"/><category term="GRPC"/><category term="Go programming"/><category term="Golang"/><category term="Hadoop MapReduce BigData"/><category term="Large Language Models"/><category term="NoSql"/><category term="cloud"/><category term="cloud computing"/><category term="database"/><category term="java Spring"/><category term="search elasticsearch apache solr database architecture"/><category term="Analytics"/><category term="Android Mobile development JAVA"/><category term="Archiecture"/><category term="BigData HBase Cassandra"/><category term="BigData NoSql Cassandra"/><category term="BigData NoSql Cassandra Architecture"/><category term="BigData NoSql HBase"/><category term="BigData NoSql Redis"/><category term="Code generation"/><category term="Concurrency control"/><category term="Data Storage"/><category term="Database MySql Distributed Scale HighAvailability Clustering"/><category term="DynamoDB"/><category term="Enterprise software"/><category term="High Availability"/><category term="JAVA Data Structures concurrency"/><category term="JAVA Design"/><category term="JAVA Hadoop MapReduce BigData"/><category term="JAVA Spring Transactions"/><category term="JAVA Web Architecture Servlets"/><category term="JAVA distributed programming network programming"/><category term="JAVA scalability"/><category term="Kafka Messaging BigData Analytics java Distributed applications"/><category term="MCP"/><category term="Machine learning"/><category term="MapReduce Hadoop &quot;distributed programming&quot;"/><category term="Microservices architecture web cloud distributed programming Dropwizard SpringBoot Docker Kubernetes"/><category term="MongoDb NoSql Database query BigData"/><category term="NewSql"/><category term="NoSql BigData HBase architecture"/><category term="NoSql Database MongoDb"/><category term="Partitioning"/><category term="Protobuf"/><category term="REST"/><category term="Scala"/><category term="Skills"/><category term="Spring JAVA distributed programming"/><category term="Spring security web"/><category term="Storm BigData realtime"/><category term="architecture web HA"/><category term="cloud vs software as a service"/><category term="concurrency"/><category term="java security shiro"/><category term="low latency"/><category term="messaging"/><category term="micro services"/><category term="network programming"/><category term="patterns"/><category term="replication"/><category term="scalability architecture database web"/><category term="scalability cloud database"/><category term="scalabiltiy"/><category term="security"/><category term="security architecture web"/><category term="webservices"/><title type='text'>Code &amp;amp; Cluster</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://khangaonkar.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default?redirect=false'/><link rel='alternate' type='text/html' href='http://khangaonkar.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default?start-index=26&amp;max-results=25&amp;redirect=false'/><author><name>Manoj Khangaonkar</name><uri>http://www.blogger.com/profile/13925926828045222969</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>99</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-5008017311510568944.post-9159101769926878004</id><published>2026-06-15T18:06:38.728-07:00</published><updated>2026-06-21T15:16:02.129-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Agentic AI"/><category scheme="http://www.blogger.com/atom/ns#" term="AI Agent"/><category scheme="http://www.blogger.com/atom/ns#" term="Artificial Intelligence"/><category scheme="http://www.blogger.com/atom/ns#" term="MCP"/><category scheme="http://www.blogger.com/atom/ns#" term="Skills"/><title type='text'>Everyone Is Arguing About MCP vs Skills vs Tools vs CLI. They&#39;re Missing the Point.</title><content type='html'>&lt;div class=&quot;flex h-svh w-screen flex-col&quot;&gt;&lt;div class=&quot;relative z-0 flex min-h-0 w-full flex-1&quot;&gt;&lt;div class=&quot;relative flex min-h-0 w-full flex-1&quot;&gt;&lt;div class=&quot;@container/main relative flex min-w-0 flex-1 flex-col -translate-y-[calc(env(safe-area-inset-bottom,0px)/2)] pt-[calc(env(safe-area-inset-bottom,0px)/2)]&quot;&gt;&lt;div class=&quot;@w-sm/main:[scrollbar-gutter:var(--stage-scroll-gutter)] touch:[scrollbar-width:none] group/scroll-root relative flex min-h-0 min-w-0 flex-1 flex-col [scrollbar-gutter:stable] not-print:overflow-x-clip not-print:overflow-y-auto group-data-stream-active/scroll-root:[overflow-anchor:none] scroll-pt-(--header-height) [--sticky-padding-top:var(--header-height)] [--sticky-padding-bottom:0px] [--scroll-root-safe-area-inset-top:calc(var(--sticky-padding-top)+env(safe-area-inset-top,0px))] [--scroll-root-safe-area-inset-bottom:calc(var(--sticky-padding-bottom)+var(--screen-keyboard-height,0px)+env(safe-area-inset-bottom,0px))] [--scroll-root-safe-area-height:calc(100lvh-var(--scroll-root-safe-area-inset-top)-var(--scroll-root-safe-area-inset-bottom))] has-data-[fixed-header=less-than-xl]:@w-xl/main:scroll-pt-0 has-data-[fixed-header=less-than-xl]:@w-xl/main:[--sticky-padding-top:0px] has-data-[fixed-header=less-than-xxl]:@w-2xl/main:scroll-pt-0 has-data-[fixed-header=less-than-xxl]:@w-2xl/main:[--sticky-padding-top:0px]&quot; data-scroll-from-top=&quot;&quot; data-scroll-root=&quot;&quot;&gt;&lt;main class=&quot;min-h-0 flex-1&quot; id=&quot;main&quot; z-index=&quot;-1&quot;&gt;&lt;div class=&quot;group/thread flex flex-col min-h-full&quot; id=&quot;thread&quot;&gt;&lt;div class=&quot;composer-parent flex flex-1 flex-col focus-visible:outline-0&quot; role=&quot;presentation&quot;&gt;&lt;div class=&quot;relative basis-auto flex-col -mb-(--composer-overlap-px) pb-(--composer-overlap-px) [--composer-overlap-px:28px] grow flex&quot;&gt;&lt;div class=&quot;flex flex-col text-sm pb-25&quot;&gt;&lt;section class=&quot;text-token-text-primary w-full focus:outline-none [--shadow-height:45px] has-data-writing-block:pointer-events-none has-data-writing-block:-mt-(--shadow-height) has-data-writing-block:pt-(--shadow-height) [&amp;amp;:has([data-writing-block])&amp;gt;*]:pointer-events-auto scroll-mt-[calc(var(--header-height)+min(200px,max(70px,20svh)))]&quot; data-scroll-anchor=&quot;true&quot; data-testid=&quot;conversation-turn-24&quot; data-turn-id=&quot;request-WEB:3d249d84-1994-4a1f-aa49-1466d84cbab8-27&quot; data-turn=&quot;assistant&quot; dir=&quot;auto&quot;&gt;&lt;div class=&quot;text-base my-auto mx-auto pb-10 [--thread-content-margin:var(--thread-content-margin-xs,calc(var(--spacing)*4))] @w-sm/main:[--thread-content-margin:var(--thread-content-margin-sm,calc(var(--spacing)*6))] @w-lg/main:[--thread-content-margin:var(--thread-content-margin-lg,calc(var(--spacing)*16))] px-(--thread-content-margin)&quot;&gt;&lt;div class=&quot;[--thread-content-max-width:40rem] @w-lg/main:[--thread-content-max-width:48rem] mx-auto max-w-(--thread-content-max-width) flex-1 group/turn-messages focus-visible:outline-hidden relative flex w-full min-w-0 flex-col agent-turn&quot;&gt;&lt;div class=&quot;flex max-w-full flex-col gap-4 grow&quot;&gt;&lt;div class=&quot;min-h-8 text-message relative flex w-full flex-col items-end gap-2 text-start break-words whitespace-normal outline-none keyboard-focused:focus-ring [.text-message+&amp;amp;]:mt-1&quot; data-message-author-role=&quot;assistant&quot; data-message-id=&quot;efd9b2d3-436f-46c5-9bd0-082b4085e605&quot; data-message-model-slug=&quot;gpt-5-5&quot; data-turn-start-message=&quot;true&quot; dir=&quot;auto&quot; tabindex=&quot;0&quot;&gt;&lt;div class=&quot;flex w-full flex-col gap-1 empty:hidden&quot;&gt;&lt;div class=&quot;markdown prose dark:prose-invert w-full wrap-break-word light markdown-new-styling&quot;&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;The agentic AI ecosystem has developed a terminology problem.&lt;/div&gt;&lt;p data-end=&quot;685&quot; data-start=&quot;555&quot;&gt;Depending on which&amp;nbsp; startup, or conference talk or group you&#39;re listening to, the future of AI agents is supposedly built around:&lt;/p&gt;&lt;ul data-end=&quot;727&quot; data-start=&quot;686&quot;&gt;&lt;li data-end=&quot;693&quot; data-section-id=&quot;177rzb7&quot; data-start=&quot;686&quot;&gt;Tools&lt;/li&gt;&lt;li data-end=&quot;702&quot; data-section-id=&quot;65hswa&quot; data-start=&quot;694&quot;&gt;Skills&lt;/li&gt;&lt;li data-end=&quot;708&quot; data-section-id=&quot;1o4c0m&quot; data-start=&quot;703&quot;&gt;MCP&lt;/li&gt;&lt;li data-end=&quot;727&quot; data-section-id=&quot;1vi1hvs&quot; data-start=&quot;709&quot;&gt;CLI environments&lt;/li&gt;&lt;/ul&gt;After building and studying agent systems for the last couple of years, I feel these debates are missing the real question.&amp;nbsp;&lt;span style=&quot;background-color: white; color: #141413;&quot;&gt;It isn&#39;t which abstraction to use.&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;markdown prose dark:prose-invert w-full wrap-break-word light markdown-new-styling&quot;&gt;&lt;span style=&quot;background-color: white; color: #141413;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class=&quot;markdown prose dark:prose-invert w-full wrap-break-word light markdown-new-styling&quot;&gt;&lt;span style=&quot;background-color: white; color: #141413;&quot;&gt;It&#39;s: where should orchestration live?&amp;nbsp;&lt;/span&gt;Once you understand that, tools, skills, MCP, and CLI all start to make sense.&lt;p data-end=&quot;1237&quot; data-start=&quot;1156&quot;&gt;To illustrate this, let&#39;s imagine we&#39;re building a fictional AI Operations Agent.&lt;/p&gt;&lt;p data-end=&quot;1253&quot; data-start=&quot;1239&quot;&gt;The agent can:&lt;/p&gt;&lt;ul data-end=&quot;1404&quot; data-start=&quot;1254&quot;&gt;&lt;li data-end=&quot;1277&quot; data-section-id=&quot;14jups&quot; data-start=&quot;1254&quot;&gt;investigate incidents&lt;/li&gt;&lt;li data-end=&quot;1304&quot; data-section-id=&quot;gdpjrb&quot; data-start=&quot;1278&quot;&gt;analyze logs and metrics&lt;/li&gt;&lt;li data-end=&quot;1325&quot; data-section-id=&quot;1dtvhfc&quot; data-start=&quot;1305&quot;&gt;review deployments&lt;/li&gt;&lt;li data-end=&quot;1347&quot; data-section-id=&quot;346wx7&quot; data-start=&quot;1326&quot;&gt;create Jira tickets&lt;/li&gt;&lt;li data-end=&quot;1370&quot; data-section-id=&quot;1c98sc8&quot; data-start=&quot;1348&quot;&gt;optimize cloud costs&lt;/li&gt;&lt;li data-end=&quot;1404&quot; data-section-id=&quot;1tp1xtc&quot; data-start=&quot;1371&quot;&gt;assist with root cause analysis&lt;/li&gt;&lt;/ul&gt;&lt;p data-end=&quot;1478&quot; data-start=&quot;1406&quot;&gt;As we&#39;ll see, the architecture evolves dramatically as the system grows.&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;The Fundamental Problem&lt;/h2&gt;&lt;p data-end=&quot;1572&quot; data-start=&quot;1512&quot;&gt;At its core, an LLM cannot actually perform operations work.&lt;/p&gt;&lt;p data-end=&quot;1584&quot; data-start=&quot;1574&quot;&gt;It cannot:&lt;/p&gt;&lt;ul data-end=&quot;1696&quot; data-start=&quot;1585&quot;&gt;&lt;li data-end=&quot;1600&quot; data-section-id=&quot;1wfie7y&quot; data-start=&quot;1585&quot;&gt;query Datadog&lt;/li&gt;&lt;li data-end=&quot;1625&quot; data-section-id=&quot;esu4yh&quot; data-start=&quot;1601&quot;&gt;inspect GitHub commits&lt;/li&gt;&lt;li data-end=&quot;1655&quot; data-section-id=&quot;1iungv1&quot; data-start=&quot;1626&quot;&gt;retrieve deployment history&lt;/li&gt;&lt;li data-end=&quot;1674&quot; data-section-id=&quot;1li7p75&quot; data-start=&quot;1656&quot;&gt;restart services&lt;/li&gt;&lt;li data-end=&quot;1696&quot; data-section-id=&quot;346wx7&quot; data-start=&quot;1675&quot;&gt;create Jira tickets&lt;/li&gt;&lt;/ul&gt;&lt;p data-end=&quot;1763&quot; data-start=&quot;1698&quot;&gt;All it can do is reason over information provided in its context.&lt;/p&gt;&lt;p data-end=&quot;1854&quot; data-start=&quot;1765&quot;&gt;Everything useful in agentic AI comes from connecting the model to external capabilities.&lt;/p&gt;&lt;p data-end=&quot;1877&quot; data-start=&quot;1856&quot;&gt;The question becomes:&lt;/p&gt;&lt;blockquote data-end=&quot;1922&quot; data-start=&quot;1879&quot;&gt;&lt;p data-end=&quot;1922&quot; data-start=&quot;1881&quot;&gt;How should those capabilities be exposed?&lt;/p&gt;&lt;/blockquote&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;The Tool-Based Agent&lt;/h2&gt;&lt;p data-end=&quot;1998&quot; data-start=&quot;1962&quot;&gt;Most agent systems start with tools.&lt;/p&gt;&lt;p data-end=&quot;2061&quot; data-start=&quot;2000&quot;&gt;A tool is simply a primitive capability exposed to the model.&lt;/p&gt;&lt;p data-end=&quot;2115&quot; data-start=&quot;2063&quot;&gt;For our operations agent, the tools might look like:&lt;/p&gt;&lt;pre class=&quot;overflow-visible! px-0!&quot; data-end=&quot;2323&quot; data-start=&quot;2117&quot;&gt;&lt;div class=&quot;relative w-full mt-4 mb-1&quot;&gt;&lt;div&gt;&lt;div class=&quot;relative&quot;&gt;&lt;div class=&quot;h-full min-h-0 min-w-0&quot;&gt;&lt;div class=&quot;h-full min-h-0 min-w-0&quot;&gt;&lt;div class=&quot;border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl&quot;&gt;&lt;div class=&quot;h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback&quot;&gt;&lt;div class=&quot;pointer-events-none absolute inset-x-4 top-12 bottom-4&quot;&gt;&lt;div class=&quot;pointer-events-none sticky z-40 shrink-0 z-1!&quot;&gt;&lt;div class=&quot;sticky bg-token-border-light&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;relative&quot;&gt;&lt;div class=&quot;w-full overflow-x-hidden overflow-y-auto&quot;&gt;&lt;div class=&quot;relative z-0 flex max-w-full&quot;&gt;&lt;div class=&quot;q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj&quot; dir=&quot;ltr&quot; id=&quot;code-block-viewer&quot;&gt;&lt;div class=&quot;cm-scroller&quot;&gt;&lt;div class=&quot;cm-content q9tKkq_readonly&quot;&gt;&lt;span class=&quot;ͼ8&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;ͼe&quot;&gt;query_logs&lt;/span&gt;(&lt;span class=&quot;ͼe&quot;&gt;service_name&lt;/span&gt;: &lt;span class=&quot;ͼe&quot;&gt;str&lt;/span&gt;):&lt;br /&gt;    ...&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;ͼ8&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;ͼe&quot;&gt;query_metrics&lt;/span&gt;(&lt;span class=&quot;ͼe&quot;&gt;service_name&lt;/span&gt;: &lt;span class=&quot;ͼe&quot;&gt;str&lt;/span&gt;):&lt;br /&gt;    ...&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;ͼ8&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;ͼe&quot;&gt;get_recent_deployments&lt;/span&gt;(&lt;span class=&quot;ͼe&quot;&gt;service_name&lt;/span&gt;: &lt;span class=&quot;ͼe&quot;&gt;str&lt;/span&gt;):&lt;br /&gt;    ...&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;ͼ8&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;ͼe&quot;&gt;create_jira_ticket&lt;/span&gt;(&lt;span class=&quot;ͼe&quot;&gt;summary&lt;/span&gt;: &lt;span class=&quot;ͼe&quot;&gt;str&lt;/span&gt;):&lt;br /&gt;    ...&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p data-end=&quot;2361&quot; data-start=&quot;2325&quot;&gt;The architecture is straightforward.&lt;/p&gt;&lt;pre class=&quot;overflow-visible! px-0!&quot; data-end=&quot;2468&quot; data-start=&quot;2363&quot;&gt;&lt;div class=&quot;relative w-full mt-4 mb-1&quot;&gt;&lt;div&gt;&lt;div class=&quot;relative&quot;&gt;&lt;div class=&quot;h-full min-h-0 min-w-0&quot;&gt;&lt;div class=&quot;h-full min-h-0 min-w-0&quot;&gt;&lt;div class=&quot;border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl&quot;&gt;&lt;div class=&quot;h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback&quot;&gt;&lt;div class=&quot;pointer-events-none absolute end-1.5 top-1 z-2 md:end-2 md:top-1&quot;&gt;&lt;/div&gt;&lt;div class=&quot;relative&quot;&gt;&lt;div class=&quot;w-full overflow-x-hidden overflow-y-auto pe-11 pt-3&quot;&gt;&lt;div class=&quot;relative z-0 flex max-w-full&quot;&gt;&lt;div class=&quot;q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj&quot; dir=&quot;ltr&quot; id=&quot;code-block-viewer&quot;&gt;&lt;div class=&quot;cm-scroller&quot;&gt;&lt;div class=&quot;cm-content q9tKkq_readonly&quot;&gt;User&lt;br /&gt; ↓&lt;br /&gt;LLM decides next action&lt;br /&gt; ↓&lt;br /&gt;Agent Runtime&lt;br /&gt; ↓&lt;br /&gt;Tool Execution&lt;br /&gt; ↓&lt;br /&gt;Results returned to LLM&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p data-end=&quot;2490&quot; data-start=&quot;2470&quot;&gt;Suppose a user asks:&lt;/p&gt;&lt;blockquote data-end=&quot;2548&quot; data-start=&quot;2492&quot;&gt;&lt;p data-end=&quot;2548&quot; data-start=&quot;2494&quot;&gt;Why is checkout-service returning elevated 500 errors?&lt;/p&gt;&lt;/blockquote&gt;&lt;p data-end=&quot;2592&quot; data-start=&quot;2550&quot;&gt;The interaction loop might look like this:&lt;br /&gt;&lt;/p&gt;&lt;ol data-end=&quot;2879&quot; data-start=&quot;2594&quot;&gt;&lt;li data-end=&quot;2634&quot; data-section-id=&quot;sjhro6&quot; data-start=&quot;2594&quot;&gt;The LLM determines that logs are required.&lt;/li&gt;&lt;li data-end=&quot;2674&quot; data-section-id=&quot;1t069od&quot; data-start=&quot;2635&quot;&gt;The runtime executes &lt;code data-end=&quot;2673&quot; data-start=&quot;2659&quot;&gt;query_logs()&lt;/code&gt;.&lt;/li&gt;&lt;li data-end=&quot;2699&quot; data-section-id=&quot;12ilsep&quot; data-start=&quot;2675&quot;&gt;Results are returned.&lt;/li&gt;&lt;li data-end=&quot;2756&quot; data-section-id=&quot;l2duu8&quot; data-start=&quot;2700&quot;&gt;The LLM determines deployment history is also needed.&lt;/li&gt;&lt;li data-end=&quot;2808&quot; data-section-id=&quot;1ul0pb&quot; data-start=&quot;2757&quot;&gt;The runtime executes &lt;code data-end=&quot;2807&quot; data-start=&quot;2781&quot;&gt;get_recent_deployments()&lt;/code&gt;.&lt;/li&gt;&lt;li data-end=&quot;2833&quot; data-section-id=&quot;185hqgk&quot; data-start=&quot;2809&quot;&gt;Results are returned.&lt;/li&gt;&lt;li data-end=&quot;2879&quot; data-section-id=&quot;y2iy6e&quot; data-start=&quot;2834&quot;&gt;The LLM generates a root cause hypothesis.&lt;/li&gt;&lt;/ol&gt;&lt;p data-end=&quot;2910&quot; data-start=&quot;2881&quot;&gt;This works surprisingly well. Initially.&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;The Hidden Scaling Problem&lt;/h3&gt;&lt;p data-end=&quot;3003&quot; data-start=&quot;2959&quot;&gt;The challenge appears when the system grows.&lt;/p&gt;&lt;p data-end=&quot;3052&quot; data-start=&quot;3005&quot;&gt;At first the agent has only a handful of tools.&lt;/p&gt;&lt;pre class=&quot;overflow-visible! px-0!&quot; data-end=&quot;3130&quot; data-start=&quot;3054&quot;&gt;&lt;div class=&quot;relative w-full mt-4 mb-1&quot;&gt;&lt;div&gt;&lt;div class=&quot;relative&quot;&gt;&lt;div class=&quot;h-full min-h-0 min-w-0&quot;&gt;&lt;div class=&quot;h-full min-h-0 min-w-0&quot;&gt;&lt;div class=&quot;border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl&quot;&gt;&lt;div class=&quot;h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback&quot;&gt;&lt;div class=&quot;pointer-events-none absolute end-1.5 top-1 z-2 md:end-2 md:top-1&quot;&gt;&lt;/div&gt;&lt;div class=&quot;relative&quot;&gt;&lt;div class=&quot;w-full overflow-x-hidden overflow-y-auto pe-11 pt-3&quot;&gt;&lt;div class=&quot;relative z-0 flex max-w-full&quot;&gt;&lt;div class=&quot;q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj&quot; dir=&quot;ltr&quot; id=&quot;code-block-viewer&quot;&gt;&lt;div class=&quot;cm-scroller&quot;&gt;&lt;div class=&quot;cm-content q9tKkq_readonly&quot;&gt;query_logs()&lt;br /&gt;query_metrics()&lt;br /&gt;query_deployments()&lt;br /&gt;create_ticket()&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p data-end=&quot;3149&quot; data-start=&quot;3132&quot;&gt;Six months later:&lt;/p&gt;&lt;pre class=&quot;overflow-visible! px-0!&quot; data-end=&quot;3354&quot; data-start=&quot;3151&quot;&gt;&lt;div class=&quot;relative w-full mt-4 mb-1&quot;&gt;&lt;div&gt;&lt;div class=&quot;relative&quot;&gt;&lt;div class=&quot;h-full min-h-0 min-w-0&quot;&gt;&lt;div class=&quot;h-full min-h-0 min-w-0&quot;&gt;&lt;div class=&quot;border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl&quot;&gt;&lt;div class=&quot;h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback&quot;&gt;&lt;div class=&quot;pointer-events-none absolute end-1.5 top-1 z-2 md:end-2 md:top-1&quot;&gt;&lt;/div&gt;&lt;div class=&quot;relative&quot;&gt;&lt;div class=&quot;w-full overflow-x-hidden overflow-y-auto pe-11 pt-3&quot;&gt;&lt;div class=&quot;relative z-0 flex max-w-full&quot;&gt;&lt;div class=&quot;q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj&quot; dir=&quot;ltr&quot; id=&quot;code-block-viewer&quot;&gt;&lt;div class=&quot;cm-scroller&quot;&gt;&lt;div class=&quot;cm-content q9tKkq_readonly&quot;&gt;query_logs()&lt;br /&gt;query_metrics()&lt;br /&gt;query_traces()&lt;br /&gt;query_alerts()&lt;br /&gt;query_runbooks()&lt;br /&gt;restart_service()&lt;br /&gt;rollback_deployment()&lt;br /&gt;create_ticket()&lt;br /&gt;query_cost_data()&lt;br /&gt;analyze_utilization()&lt;br /&gt;deploy_hotfix()&lt;br /&gt;...&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p data-end=&quot;3436&quot; data-start=&quot;3392&quot;&gt;Now the LLM is no longer merely selecting tools.&lt;/p&gt;&lt;p data-end=&quot;3482&quot; data-start=&quot;3438&quot;&gt;It is orchestrating an operational workflow.&lt;/p&gt;&lt;p data-end=&quot;3499&quot; data-start=&quot;3484&quot;&gt;It must decide:&lt;/p&gt;&lt;ul data-end=&quot;3645&quot; data-start=&quot;3500&quot;&gt;&lt;li data-end=&quot;3523&quot; data-section-id=&quot;1vfrzhx&quot; data-start=&quot;3500&quot;&gt;which tools to invoke&lt;/li&gt;&lt;li data-end=&quot;3539&quot; data-section-id=&quot;3xprzf&quot; data-start=&quot;3524&quot;&gt;in what order&lt;/li&gt;&lt;li data-end=&quot;3583&quot; data-section-id=&quot;l3okj8&quot; data-start=&quot;3540&quot;&gt;when enough information has been gathered&lt;/li&gt;&lt;li data-end=&quot;3614&quot; data-section-id=&quot;1dqdgak&quot; data-start=&quot;3584&quot;&gt;how to recover from failures&lt;/li&gt;&lt;li data-end=&quot;3645&quot; data-section-id=&quot;96ohec&quot; data-start=&quot;3615&quot;&gt;what actions should be taken&lt;/li&gt;&lt;/ul&gt;&lt;p data-end=&quot;3662&quot; data-start=&quot;3647&quot;&gt;In other words:&lt;/p&gt;&lt;blockquote data-end=&quot;3719&quot; data-start=&quot;3664&quot;&gt;&lt;p data-end=&quot;3719&quot; data-start=&quot;3666&quot;&gt;Tool-based systems push orchestration into the model.&lt;/p&gt;&lt;/blockquote&gt;&lt;p data-end=&quot;3770&quot; data-start=&quot;3721&quot;&gt;This turns out to be both powerful and dangerous.&lt;/p&gt;&lt;p data-end=&quot;3858&quot; data-start=&quot;3810&quot;&gt;The problem has three dimensions. First, reliability: too much execution logic is living inside a probabilistic system that may sequence it differently on every run. Second, cost: every invocation pays LLM inference tokens to rediscover a workflow that never changes. Third, latency: each tool-selection step is a round-trip to the model — a waterfall of LLM calls where a single deterministic function would do.&lt;/p&gt;&lt;p data-end=&quot;3858&quot; data-start=&quot;3810&quot;&gt;The sequence:&lt;/p&gt;&lt;p data-end=&quot;3858&quot; data-start=&quot;3810&quot;&gt;query logs → query metrics → retrieve deployment history → retrieve recent alerts&lt;/p&gt;&lt;p data-end=&quot;3858&quot; data-start=&quot;3810&quot;&gt;is completely deterministic. You already know exactly what to fetch. Forcing the LLM to reason its way to that sequence on every invocation is not intelligence — it&#39;s waste. Unreliable, slow, and expensive waste.&lt;/p&gt;&lt;p data-end=&quot;3858&quot; data-start=&quot;3810&quot;&gt;We are slowly realizing that not all orchestration belongs inside the model. This realization leads directly to skills.&lt;/p&gt;&lt;h2&gt;The Two Orchestration Layers&lt;/h2&gt;&lt;div&gt;&lt;div&gt;Every agent system, no matter how it&#39;s described, is really making decisions at two different layers:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Semantic orchestration — deciding what needs to happen. Which capability is relevant. What the user actually meant. This is inherently ambiguous, and it&#39;s the one job LLMs are genuinely good at.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Execution orchestration — deciding how it happens. Ordering, parallelism, retries, error handling, state. This is inherently deterministic, and it&#39;s the one job LLMs are bad at — slow, expensive, and inconsistent across runs.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Most of the confusion in this space comes from putting both layers in the same place. Tools, skills, MCP, and CLI environments are best understood as different ways of drawing the line between these two layers — not as competing abstractions. Keep this distinction in mind; every section below maps back to it.&lt;/div&gt;&lt;/div&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;span&gt;&lt;span style=&quot;font-size: small;&quot;&gt;&lt;span style=&quot;font-weight: normal;&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;/h2&gt;&lt;h2&gt;Skills: Coarse-Grained Semantic Capabilities&lt;/h2&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;font-weight: normal;&quot;&gt;&lt;span style=&quot;font-size: small;&quot;&gt;&lt;span style=&quot;color: #141413; font-family: inherit;&quot;&gt;Skills are often described vaguely, so let&#39;s define them carefully.&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #141413; font-family: inherit;&quot;&gt;A skill is not simply a larger tool. Nor is it necessarily another autonomous agent.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;box-sizing: border-box; color: #141413; margin: 0px 0px 10px; padding: 0px; text-align: left;&quot;&gt;&lt;p style=&quot;box-sizing: border-box; margin: 0px 0px 10px; padding: 0px; text-align: left;&quot;&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;More precisely: a skill is a subgraph with a semantic boundary. It encapsulates three things the LLM should not have to think about: execution order, parallelism, and retry policy. The planner sees a named capability. The skill owns how that capability runs.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin: 0px 0px 10px; padding: 0px; text-align: left;&quot;&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;For our operations platform, skills might include: IncidentAnalysisSkill, DeploymentReviewSkill, CapacityPlanningSkill, CostOptimizationSkill.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;box-sizing: border-box; margin: 0px 0px 10px; padding: 0px; text-align: left;&quot;&gt;&lt;span style=&quot;font-family: inherit;&quot;&gt;Tools describe primitive operations. Skills describe business-level capabilities and the execution policy that implements them.&lt;/span&gt;&lt;/p&gt;&lt;/div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;The Planner LLM Still Matters&lt;/h3&gt;&lt;p data-end=&quot;5160&quot; data-start=&quot;5083&quot;&gt;Without an LLM, mapping human requests to skills becomes extremely difficult.&lt;/p&gt;&lt;p data-end=&quot;5171&quot; data-start=&quot;5162&quot;&gt;Consider:&lt;/p&gt;&lt;blockquote data-end=&quot;5226&quot; data-start=&quot;5173&quot;&gt;&lt;p data-end=&quot;5226&quot; data-start=&quot;5175&quot;&gt;Why did checkout-service start failing after lunch?&lt;/p&gt;&lt;/blockquote&gt;&lt;p data-end=&quot;5236&quot; data-start=&quot;5228&quot;&gt;Is this:&lt;/p&gt;&lt;ul data-end=&quot;5314&quot; data-start=&quot;5237&quot;&gt;&lt;li data-end=&quot;5265&quot; data-section-id=&quot;xs6nla&quot; data-start=&quot;5237&quot;&gt;an incident investigation?&lt;/li&gt;&lt;li data-end=&quot;5288&quot; data-section-id=&quot;195zmd1&quot; data-start=&quot;5266&quot;&gt;a deployment review?&lt;/li&gt;&lt;li data-end=&quot;5314&quot; data-section-id=&quot;dzs8l8&quot; data-start=&quot;5289&quot;&gt;a performance analysis?&lt;/li&gt;&lt;/ul&gt;&lt;p data-end=&quot;5356&quot; data-start=&quot;5316&quot;&gt;The ambiguity lives in natural language.&lt;/p&gt;&lt;p data-end=&quot;5395&quot; data-start=&quot;5358&quot;&gt;The LLM remains extremely useful for:&lt;/p&gt;&lt;ul data-end=&quot;5465&quot; data-start=&quot;5396&quot;&gt;&lt;li data-end=&quot;5418&quot; data-section-id=&quot;6nxu5c&quot; data-start=&quot;5396&quot;&gt;intent understanding&lt;/li&gt;&lt;li data-end=&quot;5437&quot; data-section-id=&quot;pnhvr2&quot; data-start=&quot;5419&quot;&gt;semantic routing&lt;/li&gt;&lt;li data-end=&quot;5465&quot; data-section-id=&quot;1c90n9h&quot; data-start=&quot;5438&quot;&gt;contextual interpretation&lt;/li&gt;&lt;/ul&gt;&lt;p data-end=&quot;5505&quot; data-start=&quot;5467&quot;&gt;The architecture increasingly becomes:&lt;/p&gt;&lt;pre class=&quot;overflow-visible! px-0!&quot; data-end=&quot;5622&quot; data-start=&quot;5507&quot;&gt;&lt;div class=&quot;relative w-full mt-4 mb-1&quot;&gt;&lt;div&gt;&lt;div class=&quot;relative&quot;&gt;&lt;div class=&quot;h-full min-h-0 min-w-0&quot;&gt;&lt;div class=&quot;h-full min-h-0 min-w-0&quot;&gt;&lt;div class=&quot;border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl&quot;&gt;&lt;div class=&quot;h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback&quot;&gt;&lt;div class=&quot;pointer-events-none absolute end-1.5 top-1 z-2 md:end-2 md:top-1&quot;&gt;&lt;/div&gt;&lt;div class=&quot;relative&quot;&gt;&lt;div class=&quot;w-full overflow-x-hidden overflow-y-auto pe-11 pt-3&quot;&gt;&lt;div class=&quot;relative z-0 flex max-w-full&quot;&gt;&lt;div class=&quot;q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj&quot; dir=&quot;ltr&quot; id=&quot;code-block-viewer&quot;&gt;&lt;div class=&quot;cm-scroller&quot;&gt;&lt;div class=&quot;cm-content q9tKkq_readonly&quot;&gt;User Request&lt;br /&gt; ↓&lt;br /&gt;Planner LLM&lt;br /&gt; ↓&lt;br /&gt;Skill Selection&lt;br /&gt; ↓&lt;br /&gt;Skill Execution&lt;br /&gt; ↓&lt;br /&gt;Results&lt;br /&gt; ↓&lt;br /&gt;Planner LLM&lt;br /&gt; ↓&lt;br /&gt;Response&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Skills Move Execution Orchestration Out Of The Model&lt;/h3&gt;&lt;p data-end=&quot;5748&quot; data-start=&quot;5685&quot;&gt;Skills are the clearest example of this split in action: semantic orchestration stays with the planner, execution orchestration moves into the skill. This is the architectural shift many modern systems are making.&lt;/p&gt;&lt;p data-end=&quot;5762&quot; data-start=&quot;5750&quot;&gt;For example:&lt;/p&gt;&lt;pre class=&quot;overflow-visible! px-0!&quot; data-end=&quot;6271&quot; data-start=&quot;5764&quot;&gt;&lt;div class=&quot;relative w-full mt-4 mb-1&quot;&gt;&lt;div class=&quot;relative&quot;&gt;&lt;div class=&quot;h-full min-h-0 min-w-0&quot;&gt;&lt;div class=&quot;h-full min-h-0 min-w-0&quot;&gt;&lt;div class=&quot;border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl&quot;&gt;&lt;div class=&quot;h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback&quot;&gt;&lt;div class=&quot;pointer-events-none absolute inset-x-4 top-12 bottom-4&quot;&gt;&lt;div class=&quot;pointer-events-none sticky z-40 shrink-0 z-1!&quot;&gt;&lt;div class=&quot;sticky bg-token-border-light&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;relative&quot;&gt;&lt;div class=&quot;w-full overflow-x-hidden overflow-y-auto&quot;&gt;&lt;div class=&quot;relative z-0 flex max-w-full&quot;&gt;&lt;div class=&quot;q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj&quot; dir=&quot;ltr&quot; id=&quot;code-block-viewer&quot;&gt;&lt;div class=&quot;cm-scroller&quot;&gt;&lt;div class=&quot;cm-content q9tKkq_readonly&quot;&gt;class IncidentAnalysisSkill:

  async def execute(self, service_name):

    # Fetch concurrently — no reason to serialize
    logs, metrics, deployments, alerts = (
      await asyncio.gather(
        query_logs(service_name),
        query_metrics(service_name),
        get_recent_deployments(service_name),
        get_recent_alerts(service_name)
      )
    )

    return {
      &quot;logs&quot;: logs,
      &quot;metrics&quot;: metrics,
      &quot;deployments&quot;: deployments,
      &quot;alerts&quot;: alerts
    }&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p data-end=&quot;6294&quot; data-start=&quot;6273&quot;&gt;The planner LLM still decides: I need IncidentAnalysisSkill. But once selected, deterministic execution logic takes over — including decisions the LLM would never make well, like whether to fetch in parallel or how to handle a partial failure.&lt;/p&gt;&lt;p data-end=&quot;6294&quot; data-start=&quot;6273&quot;&gt;Note: this example is deliberately simplified. In production, skills typically need streaming partial results, per-tool timeouts, and circuit breakers. The point is that all of that complexity lives in the skill — not in the model.&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Where MCP Fits&lt;/h2&gt;&lt;p data-end=&quot;7037&quot; data-start=&quot;6983&quot;&gt;MCP doesn&#39;t touch this split at all — it just makes the capabilities on the execution side portable across runtimes. This is where many online discussions become confused.&lt;/p&gt;&lt;p data-end=&quot;7072&quot; data-start=&quot;7039&quot;&gt;MCP is not competing with skills.&lt;/p&gt;&lt;p data-end=&quot;7116&quot; data-start=&quot;7074&quot;&gt;MCP solves a completely different problem.&lt;/p&gt;&lt;p data-end=&quot;7177&quot; data-start=&quot;7118&quot;&gt;The ecosystem currently suffers from massive fragmentation.&lt;/p&gt;&lt;p data-end=&quot;7203&quot; data-start=&quot;7179&quot;&gt;Every framework invents:&lt;/p&gt;&lt;ul data-end=&quot;7294&quot; data-start=&quot;7204&quot;&gt;&lt;li data-end=&quot;7228&quot; data-section-id=&quot;1j7fywn&quot; data-start=&quot;7204&quot;&gt;different tool schemas&lt;/li&gt;&lt;li data-end=&quot;7262&quot; data-section-id=&quot;58waat&quot; data-start=&quot;7229&quot;&gt;different invocation mechanisms&lt;/li&gt;&lt;li data-end=&quot;7294&quot; data-section-id=&quot;b2yki&quot; data-start=&quot;7263&quot;&gt;different discovery protocols&lt;/li&gt;&lt;/ul&gt;&lt;p data-end=&quot;7335&quot; data-start=&quot;7296&quot;&gt;MCP attempts to standardize that layer.&lt;/p&gt;&lt;p data-end=&quot;7375&quot; data-start=&quot;7337&quot;&gt;The easiest way to think about MCP is:&lt;/p&gt;&lt;blockquote data-end=&quot;7450&quot; data-start=&quot;7377&quot;&gt;&lt;p data-end=&quot;7450&quot; data-start=&quot;7379&quot;&gt;MCP is trying to become the interoperability layer for AI capabilities.&lt;/p&gt;&lt;/blockquote&gt;&lt;p data-end=&quot;7490&quot; data-start=&quot;7452&quot;&gt;Closer to OpenAPI for agent ecosystems — a shared interface contract that defines how capabilities are described, discovered, and invoked, without caring what carries the bytes underneath.&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;What MCP Exposes&lt;/h3&gt;&lt;p data-end=&quot;7577&quot; data-start=&quot;7532&quot;&gt;Most coverage of MCP focuses only on tools. But the protocol defines three primitives, and the distinction matters:&lt;/p&gt;&lt;p data-end=&quot;7577&quot; data-start=&quot;7532&quot;&gt;Tools — callable functions the model can invoke. This is what most people mean when they talk about MCP.&lt;/p&gt;&lt;p data-end=&quot;7577&quot; data-start=&quot;7532&quot;&gt;Resources — structured data the model can read: log files, database records, API responses. Resources are pull-based; the model requests them rather than the server pushing them.&lt;/p&gt;&lt;p data-end=&quot;7577&quot; data-start=&quot;7532&quot;&gt;Prompts — reusable prompt templates with parameters, surfaced by the server. This lets server authors encode domain knowledge — e.g., a Datadog MCP server can expose a prompt template for structured incident analysis, not just raw metric endpoints.&lt;/p&gt;&lt;p data-end=&quot;7577&quot; data-start=&quot;7532&quot;&gt;For our operations agent, a well-designed Datadog MCP server doesn&#39;t just expose query_metrics(). It might also expose a resource for the current service topology and a prompt template that structures how the model should reason about an anomaly. That&#39;s a qualitatively richer integration than a tool-only mental model suggests.&lt;/p&gt;&lt;p data-end=&quot;7577&quot; data-start=&quot;7532&quot;&gt;from mcp.server import Server&lt;br /&gt;server = Server(&quot;datadog-server&quot;)&lt;/p&gt;&lt;p data-end=&quot;7577&quot; data-start=&quot;7532&quot;&gt;@server.tool()&lt;br /&gt;async def query_service_metrics(service):&lt;/p&gt;&lt;p data-end=&quot;7577&quot; data-start=&quot;7532&quot;&gt;&amp;nbsp; &amp;nbsp; ...&lt;/p&gt;&lt;p data-end=&quot;7577&quot; data-start=&quot;7532&quot;&gt;@server.resource(&quot;topology://services&quot;)&lt;br /&gt;async def get_service_topology():&lt;br /&gt;&amp;nbsp; &amp;nbsp;...&lt;/p&gt;&lt;p data-end=&quot;7577&quot; data-start=&quot;7532&quot;&gt;@server.prompt()&lt;br /&gt;async def analyze_anomaly(service, window):&lt;br /&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;return f&quot;Given metrics for {service} over {window}&quot;&lt;/p&gt;&lt;p data-end=&quot;8555&quot; data-start=&quot;8518&quot;&gt;&lt;i&gt;One practical implication: an MCP server built today for Claude can work tomorrow with any other MCP-compatible runtime. That portability is the actual value proposition — not the protocol mechanics themselves.&lt;/i&gt;&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Why Coding Agents Love CLI Environments&lt;/h2&gt;&lt;p data-end=&quot;8667&quot; data-start=&quot;8605&quot;&gt;Now let&#39;s discuss the most interesting trend in the ecosystem.&amp;nbsp;CLI environments push the line further than either tools or skills: here, the model itself participates in execution orchestration by writing commands, which is exactly why sandboxing becomes non-negotiable.&lt;/p&gt;&lt;p data-end=&quot;8753&quot; data-start=&quot;8669&quot;&gt;Coding and operations agents are increasingly bypassing structured tools altogether.&lt;/p&gt;&lt;p data-end=&quot;8775&quot; data-start=&quot;8755&quot;&gt;Instead of exposing:&lt;/p&gt;&lt;pre class=&quot;overflow-visible! px-0!&quot; data-end=&quot;8836&quot; data-start=&quot;8777&quot;&gt;&lt;div class=&quot;relative w-full mt-4 mb-1&quot;&gt;&lt;div&gt;&lt;div class=&quot;relative&quot;&gt;&lt;div class=&quot;h-full min-h-0 min-w-0&quot;&gt;&lt;div class=&quot;h-full min-h-0 min-w-0&quot;&gt;&lt;div class=&quot;border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl&quot;&gt;&lt;div class=&quot;h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback&quot;&gt;&lt;div class=&quot;pointer-events-none absolute inset-x-4 top-12 bottom-4&quot;&gt;&lt;div class=&quot;pointer-events-none sticky z-40 shrink-0 z-1!&quot;&gt;&lt;div class=&quot;sticky bg-token-border-light&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;relative&quot;&gt;&lt;div class=&quot;w-full overflow-x-hidden overflow-y-auto&quot;&gt;&lt;div class=&quot;relative z-0 flex max-w-full&quot;&gt;&lt;div class=&quot;q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj&quot; dir=&quot;ltr&quot; id=&quot;code-block-viewer&quot;&gt;&lt;div class=&quot;cm-scroller&quot;&gt;&lt;div class=&quot;cm-content q9tKkq_readonly&quot;&gt;&lt;span class=&quot;ͼe&quot;&gt;query_git_diff&lt;/span&gt;()&lt;br /&gt;&lt;span class=&quot;ͼe&quot;&gt;run_tests&lt;/span&gt;()&lt;br /&gt;&lt;span class=&quot;ͼe&quot;&gt;deploy_service&lt;/span&gt;()&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p data-end=&quot;8850&quot; data-start=&quot;8838&quot;&gt;they expose:&lt;/p&gt;&lt;pre class=&quot;overflow-visible! px-0!&quot; data-end=&quot;8899&quot; data-start=&quot;8852&quot;&gt;&lt;div class=&quot;relative w-full mt-4 mb-1&quot;&gt;&lt;div&gt;&lt;div class=&quot;relative&quot;&gt;&lt;div class=&quot;h-full min-h-0 min-w-0&quot;&gt;&lt;div class=&quot;h-full min-h-0 min-w-0&quot;&gt;&lt;div class=&quot;border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl&quot;&gt;&lt;div class=&quot;h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback&quot;&gt;&lt;div class=&quot;pointer-events-none absolute end-1.5 top-1 z-2 md:end-2 md:top-1&quot;&gt;&lt;/div&gt;&lt;div class=&quot;relative&quot;&gt;&lt;div class=&quot;w-full overflow-x-hidden overflow-y-auto pe-11 pt-3&quot;&gt;&lt;div class=&quot;relative z-0 flex max-w-full&quot;&gt;&lt;div class=&quot;q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj&quot; dir=&quot;ltr&quot; id=&quot;code-block-viewer&quot;&gt;&lt;div class=&quot;cm-scroller&quot;&gt;&lt;div class=&quot;cm-content q9tKkq_readonly&quot;&gt;git&lt;br /&gt;docker&lt;br /&gt;kubectl&lt;br /&gt;terraform&lt;br /&gt;pytest&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p data-end=&quot;8940&quot; data-start=&quot;8901&quot;&gt;The shell becomes the capability layer.&lt;/p&gt;&lt;p data-end=&quot;8980&quot; data-start=&quot;8942&quot;&gt;The agent generates commands directly.&lt;/p&gt;&lt;pre class=&quot;overflow-visible! px-0!&quot; data-end=&quot;9047&quot; data-start=&quot;8982&quot;&gt;&lt;div class=&quot;relative w-full mt-4 mb-1&quot;&gt;&lt;div&gt;&lt;div class=&quot;relative&quot;&gt;&lt;div class=&quot;h-full min-h-0 min-w-0&quot;&gt;&lt;div class=&quot;h-full min-h-0 min-w-0&quot;&gt;&lt;div class=&quot;border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl&quot;&gt;&lt;div class=&quot;h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback&quot;&gt;&lt;div class=&quot;pointer-events-none absolute inset-x-4 top-12 bottom-4&quot;&gt;&lt;div class=&quot;pointer-events-none sticky z-40 shrink-0 z-1!&quot;&gt;&lt;div class=&quot;sticky bg-token-border-light&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;relative&quot;&gt;&lt;div class=&quot;w-full overflow-x-hidden overflow-y-auto&quot;&gt;&lt;div class=&quot;relative z-0 flex max-w-full&quot;&gt;&lt;div class=&quot;q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj&quot; dir=&quot;ltr&quot; id=&quot;code-block-viewer&quot;&gt;&lt;div class=&quot;cm-scroller&quot;&gt;&lt;div class=&quot;cm-content q9tKkq_readonly&quot;&gt;kubectl logs checkout-service&lt;br /&gt;&lt;span class=&quot;ͼd&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;ͼd&quot;&gt;diff&lt;/span&gt;&lt;br /&gt;terraform plan&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p data-end=&quot;9090&quot; data-start=&quot;9049&quot;&gt;This is a radically different philosophy.&lt;/p&gt;&lt;p data-end=&quot;9176&quot; data-start=&quot;9092&quot;&gt;Instead of carefully defining capabilities, you expose a real execution environment.&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Why CLI Agents Feel More Powerful&lt;/h3&gt;&lt;p data-end=&quot;9241&quot; data-start=&quot;9220&quot;&gt;Unix environments already solved composability decades ago. Commands naturally chain together. The shell already provides: composition, state, interoperability, flexibility.&lt;/p&gt;&lt;p data-end=&quot;9241&quot; data-start=&quot;9220&quot;&gt;Instead of creating hundreds of tool schemas, the environment itself becomes the API. This is why modern coding agents often feel dramatically more capable than traditional tool-calling agents.&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;The Tradeoff&lt;/h3&gt;&lt;p data-end=&quot;9241&quot; data-start=&quot;9220&quot;&gt;Of course, flexibility comes with risk. A structured tool:&lt;/p&gt;&lt;p data-end=&quot;9241&quot; data-start=&quot;9220&quot;&gt;restart_service(service_name)&lt;br /&gt;&lt;br /&gt;&amp;nbsp;is easier to constrain than:&lt;br /&gt;&lt;br /&gt;rm -rf /&lt;/p&gt;&lt;p data-end=&quot;9241&quot; data-start=&quot;9220&quot;&gt;This is not a theoretical concern. A CLI agent operating against a production Kubernetes cluster with broad permissions is one hallucinated flag away from an outage. The blast radius of a mistake is bounded only by what the execution environment allows — not by what the tool schema permits. With structured tools, you constrain at the interface. With CLI, you constrain at the environment.&lt;/p&gt;&lt;p data-end=&quot;9241&quot; data-start=&quot;9220&quot;&gt;This is why sandboxed execution environments have become infrastructure in their own right. Tools like E2B, Daytona, and Modal provide ephemeral, isolated runtimes specifically built for agent workloads: short-lived containers with scoped filesystem access, network egress controls, and resource limits. The architecture shifts from &quot;give the agent a shell&quot; to &quot;give the agent a shell inside a box it can&#39;t break out of.&quot; This also changes the observability story — a sandboxed run produces a complete audit trail of every command executed, which structured tool-calling often doesn&#39;t.&lt;/p&gt;&lt;p data-end=&quot;9241&quot; data-start=&quot;9220&quot;&gt;The deeper implication for our orchestration framework: CLI agents are the one place where the semantic/execution boundary genuinely blurs. The model is no longer just selecting a capability — it is participating in execution orchestration by writing the commands themselves. That is both the source of their power and the reason the safety layer beneath them has to be more sophisticated than a permission flag.Where The Industry Appears To Be Heading&lt;/p&gt;&lt;p data-end=&quot;10053&quot; data-start=&quot;9982&quot;&gt;If you step back from the terminology wars, a pattern begins to emerge.&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Modern agent systems increasingly look like this:&lt;/h2&gt;&lt;pre class=&quot;overflow-visible! px-0!&quot; data-end=&quot;10433&quot; data-start=&quot;10106&quot;&gt;&lt;div class=&quot;relative w-full mt-4 mb-1&quot;&gt;&lt;div&gt;&lt;div class=&quot;relative&quot;&gt;&lt;div class=&quot;h-full min-h-0 min-w-0&quot;&gt;&lt;div class=&quot;h-full min-h-0 min-w-0&quot;&gt;&lt;div class=&quot;border border-token-border-light border-radius-3xl corner-superellipse/1.1 rounded-3xl&quot;&gt;&lt;div class=&quot;h-full w-full border-radius-3xl bg-token-bg-elevated-secondary corner-superellipse/1.1 overflow-clip rounded-3xl lxnfua_clipPathFallback&quot;&gt;&lt;div class=&quot;pointer-events-none absolute end-1.5 top-1 z-2 md:end-2 md:top-1&quot;&gt;&lt;/div&gt;&lt;div class=&quot;relative&quot;&gt;&lt;div class=&quot;w-full overflow-x-hidden overflow-y-auto pe-11 pt-3&quot;&gt;&lt;div class=&quot;relative z-0 flex max-w-full&quot;&gt;&lt;div class=&quot;q9tKkq_viewer cm-editor z-10 light:cm-light dark:cm-light flex h-full w-full flex-col items-stretch ͼ5 ͼj&quot; dir=&quot;ltr&quot; id=&quot;code-block-viewer&quot;&gt;&lt;div class=&quot;cm-scroller&quot;&gt;&lt;div class=&quot;cm-content q9tKkq_readonly&quot;&gt;                 LLM&lt;br /&gt;                  ↓&lt;br /&gt;        Semantic Planning&lt;br /&gt;                  ↓&lt;br /&gt;               Skills&lt;br /&gt;                  ↓&lt;br /&gt;      Deterministic Workflows&lt;br /&gt;            ↓          ↓&lt;br /&gt;         Tools       MCP&lt;br /&gt;            ↓          ↓&lt;br /&gt;      Local Systems  External Systems&lt;br /&gt;                  ↓&lt;br /&gt;            CLI Runtime&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p data-end=&quot;10456&quot; data-start=&quot;10435&quot;&gt;Notice what happened.&lt;/p&gt;&lt;p data-end=&quot;10492&quot; data-start=&quot;10458&quot;&gt;The LLM is not &quot;the system.&quot;&lt;/p&gt;&lt;p data-end=&quot;10551&quot; data-start=&quot;10494&quot;&gt;It has become one component inside a larger architecture.&lt;/p&gt;&lt;p data-end=&quot;10585&quot; data-start=&quot;10553&quot;&gt;Reasoning remains probabilistic.&lt;/p&gt;&lt;p data-end=&quot;10619&quot; data-start=&quot;10587&quot;&gt;Execution becomes deterministic.&lt;/p&gt;&lt;p data-end=&quot;10662&quot; data-start=&quot;10621&quot;&gt;Interoperability becomes protocol-driven.&lt;/p&gt;&lt;p data-end=&quot;10708&quot; data-start=&quot;10664&quot;&gt;Capabilities become reusable infrastructure.&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Final Thoughts&lt;/h2&gt;&lt;p data-end=&quot;10812&quot; data-start=&quot;10733&quot;&gt;Tools, Skills, MCP, and CLI environments are different answers to where the line falls between semantic and execution orchestration — not competing abstractions.&lt;/p&gt;&lt;p data-end=&quot;10858&quot; data-start=&quot;10814&quot;&gt;They exist at different layers of the stack.&lt;/p&gt;&lt;p data-end=&quot;10896&quot; data-start=&quot;10860&quot;&gt;Tools expose primitive capabilities.&lt;/p&gt;&lt;p data-end=&quot;10934&quot; data-start=&quot;10898&quot;&gt;Skills expose semantic capabilities.&lt;/p&gt;&lt;p data-end=&quot;10970&quot; data-start=&quot;10936&quot;&gt;MCP standardizes interoperability.&lt;/p&gt;&lt;p data-end=&quot;11019&quot; data-start=&quot;10972&quot;&gt;CLI environments expose raw execution contexts.&lt;/p&gt;&lt;p data-end=&quot;11060&quot; data-start=&quot;11021&quot;&gt;The real architectural question is not:&lt;/p&gt;&lt;blockquote data-end=&quot;11087&quot; data-start=&quot;11062&quot;&gt;&lt;p data-end=&quot;11087&quot; data-start=&quot;11064&quot;&gt;Which abstraction wins?&lt;/p&gt;&lt;/blockquote&gt;&lt;p data-end=&quot;11095&quot; data-start=&quot;11089&quot;&gt;It is:&lt;/p&gt;&lt;blockquote data-end=&quot;11131&quot; data-start=&quot;11097&quot;&gt;&lt;p data-end=&quot;11131&quot; data-start=&quot;11099&quot;&gt;Where should orchestration live?&lt;/p&gt;&lt;/blockquote&gt;&lt;p data-end=&quot;11273&quot; data-start=&quot;11133&quot;&gt;The most successful systems increasingly place semantic orchestration inside LLMs and execution orchestration inside deterministic software.&lt;/p&gt;&lt;p data-end=&quot;11574&quot; data-start=&quot;11375&quot;&gt;The future of agentic AI looks less like autonomous chatbots wandering through APIs and more like carefully engineered systems where probabilistic reasoning and deterministic execution work together.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;z-0 flex min-h-[46px] justify-start&quot;&gt;&lt;/div&gt;&lt;div class=&quot;mt-3 w-full empty:hidden&quot;&gt;&lt;div class=&quot;text-center&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/section&gt;&lt;/div&gt;&lt;div aria-hidden=&quot;true&quot; class=&quot;pointer-events-none h-px w-px absolute bottom-0&quot; data-edge=&quot;true&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&quot;sticky bottom-0 z-10 group/thread-bottom-container relative isolate w-full basis-auto has-data-has-thread-error:pt-2 has-data-has-thread-error:[box-shadow:var(--sharp-edge-bottom-shadow)] md:border-transparent md:pt-0 dark:border-white/20 md:dark:border-transparent print:hidden content-fade single-line flex flex-col&quot; id=&quot;thread-bottom-container&quot;&gt;&lt;div class=&quot;relative mx-auto h-0&quot;&gt;&lt;/div&gt;&lt;div id=&quot;thread-bottom&quot;&gt;&lt;div class=&quot;text-base mx-auto [--thread-content-margin:var(--thread-content-margin-xs,calc(var(--spacing)*4))] @w-sm/main:[--thread-content-margin:var(--thread-content-margin-sm,calc(var(--spacing)*6))] @w-lg/main:[--thread-content-margin:var(--thread-content-margin-lg,calc(var(--spacing)*16))] px-(--thread-content-margin)&quot;&gt;&lt;div class=&quot;[--thread-content-max-width:40rem] @w-lg/main:[--thread-content-max-width:48rem] mx-auto max-w-(--thread-content-max-width) flex-1 mb-[var(--thread-component-gap,1rem)]&quot;&gt;&lt;div class=&quot;flex justify-center empty:hidden&quot;&gt;&lt;/div&gt;&lt;div class=&quot;pointer-events-auto relative z-1 flex h-(--composer-container-height,100%) max-w-full flex-(--composer-container-flex,1) flex-col&quot;&gt;&lt;div class=&quot;absolute start-0 end-0 bottom-full z-20&quot;&gt;&lt;/div&gt;&lt;form class=&quot;group/composer w-full&quot; data-type=&quot;unified-composer&quot;&gt;&lt;div class=&quot;hidden&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;/form&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/main&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/9159101769926878004'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/9159101769926878004'/><link rel='alternate' type='text/html' href='http://khangaonkar.blogspot.com/2026/06/everyone-is-arguing-about-mcp-vs-skills.html' title='Everyone Is Arguing About MCP vs Skills vs Tools vs CLI. They&#39;re Missing the Point.'/><author><name>Manoj Khangaonkar</name><uri>http://www.blogger.com/profile/13925926828045222969</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-5008017311510568944.post-6505099001302217406</id><published>2026-04-09T17:22:00.000-07:00</published><updated>2026-04-12T14:02:23.185-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Agentic AI"/><category scheme="http://www.blogger.com/atom/ns#" term="AI"/><category scheme="http://www.blogger.com/atom/ns#" term="Enterprise software"/><category scheme="http://www.blogger.com/atom/ns#" term="Large Language Models"/><title type='text'>Why Deterministic AI Agents Are The Wrong Goal ?</title><content type='html'>&lt;h2 style=&quot;text-align: left;&quot;&gt;1. The Probabilistic Machine&lt;/h2&gt;&lt;div&gt;&lt;div&gt;In the rush to build &quot;enterprise-grade&quot; AI agents, many teams are chasing a seductive idea: what if we could make AI fully deterministic — predictable, repeatable, always correct?&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;It sounds reasonable. That&#39;s how traditional software works. But it starts with a fundamental misunderstanding of what these systems actually are.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Large Language Models are not knowledge databases or rules engines. At their core, they are statistical machines — predicting the probability of the next token in a sequence, over and over, until a response takes shape. When you craft a precise prompt or inject carefully curated context, you are not overriding that mechanism. You are nudging it. Good context narrows the probability distribution and raises the likelihood of a useful answer. But it doesn&#39;t change the fundamental nature of the system. The model remains probabilistic. You can engineer the inputs to the ceiling — you cannot engineer your way out of uncertainty.&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;2. The Enterprise Trap&lt;/h2&gt;&lt;div&gt;This creates a real tension — because the properties that make LLMs powerful are almost perfectly opposed to what enterprises want from software.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;Enterprises don&#39;t want suggestions. They want answers. They don&#39;t want &quot;usually correct&quot; — they want auditable, repeatable, defensible outputs. When something goes wrong, someone needs to explain exactly why the system did what it did.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;A probabilistic system doesn&#39;t give you that cleanly. And so the instinct is to reach for determinism — to constrain the model until it behaves like a well-behaved service. Temperature to zero. Rigid output schemas. Exhaustive prompt engineering. Rules stacked on rules.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This isn&#39;t irrational. Repeatability, auditability, compliance — these are legitimate needs. But chasing determinism in the model itself is solving the wrong problem. You end up with a system that is neither reliably deterministic nor making full use of what the model can actually do. You&#39;ve neutered the intelligence without gaining the guarantees you wanted.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The goal shouldn&#39;t be a deterministic model. It should be a reliable system. Those are not the same thing — and confusing them is where most enterprise AI projects go wrong.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;3. What Actually Works: Hybrid Architecture&lt;/h2&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;The real breakthrough of modern AI systems wasn&#39;t just model quality. It was a design philosophy.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Tools like ChatGPT and Claude succeeded not because they eliminated uncertainty, but because they made uncertainty part of the interaction. They don&#39;t say &quot;here is the correct answer.&quot; They say &quot;here&#39;s a strong answer — want me to refine it?&quot; That subtle shift changes everything. The human stays in the loop. The model doesn&#39;t pretend to be infallible. And because of that, users actually trust the output enough to act on it.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This points toward the pattern that wins in production: a hybrid architecture where determinism and intelligence each live where they belong.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The structure looks like this. A deterministic shell handles everything that must be correct and repeatable — workflows, APIs, validation rules, policy enforcement. A probabilistic core handles everything that requires reasoning — summarization, analysis, decision support, generation. And control points sit between them — confidence thresholds, structured outputs, human approvals — managing the boundary between the two.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The LLM proposes. The system validates. The human, when needed, decides.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Determinism doesn&#39;t disappear. It moves to where it actually belongs.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;4. Vestra: A Concrete Example&lt;/h2&gt;&lt;div&gt;&lt;div&gt;To make this real, consider &lt;a href=&quot;https://vestrai.app&quot; target=&quot;_blank&quot;&gt;Vestra&lt;/a&gt; — an AI-powered investment analysis agent I&#39;ve been building.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;A naive approach would let the LLM do everything: fetch market data, apply financial rules, generate investment decisions. That system would fail badly. It would hallucinate stock picks, misinterpret regulations, produce outputs that couldn&#39;t survive a compliance review.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So Vestra is deliberately split into three layers.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The deterministic shell ingests user portfolio data, computes financial metrics, and enforces business rules. This is traditional code. It behaves identically every time.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The probabilistic core — the LLM — receives only clean, verified data from that shell. It doesn&#39;t touch raw market feeds. It reasons over what it&#39;s given: evaluating the portfolio against the user&#39;s goals, time horizon, and macro context, and generating high-level strategic insights. Rebalance allocations. Increase international exposure. Add diversified index funds.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The control points manage what happens next. The LLM is constrained to return structured JSON, so downstream code can validate and process the output safely. And Vestra never acts autonomously — it presents recommendations to the user with clear disclosure that they are AI-generated. The user accepts, rejects, or refines them, and is encouraged to seek professional advice for significant decisions.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The LLM doesn&#39;t fetch data. It doesn&#39;t execute trades. It doesn&#39;t make final calls.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Determinism lives in the code. Intelligence lives in the model. Control lives with the user.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;5. The Broader Principle&lt;/h2&gt;&lt;div&gt;&lt;div&gt;There&#39;s a persistent belief that keeping humans in the loop is a temporary crutch — something to tolerate until the models get good enough to go fully autonomous. That belief keeps getting disproven in production.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Human involvement isn&#39;t a limitation to engineer around. It&#39;s a design pattern that makes systems more accurate and more trusted. Users who can steer, refine, and push back on AI outputs consistently get better results than users handed a black-box decision. The interaction is where the value compounds.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;That said, not every system should work this way. Where the stakes are high but the timeline allows deliberation — investment analysis, legal drafting, code review, customer support — interactivity is a feature, not a compromise. The human brings judgment the model lacks; the model brings breadth the human can&#39;t match.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;But in real-time fraud detection, high-throughput automation, or millisecond trading systems, that loop collapses. There&#39;s no time for human approval. These systems need hard rules and hard thresholds, with AI informing the design rather than driving the execution.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The mistake isn&#39;t choosing the wrong architecture. It&#39;s assuming one architecture fits everything.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;6. The Real Opportunity&lt;/h2&gt;&lt;div&gt;&lt;div&gt;A year or two ago, the bold prediction was that autonomous agents would replace entire workflows by now. That hasn&#39;t happened — not because the models aren&#39;t capable enough, but because the systems around them weren&#39;t designed for it. Fully autonomous agents keep failing in the same ways: confidently wrong outputs, no graceful degradation, users who don&#39;t trust them enough to act on what they produce.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;What&#39;s actually working is quieter and less glamorous. Systems that surface their own uncertainty. Systems where the human is a genuine collaborator, not an afterthought. Systems where the experience is designed so carefully that raw model capability almost becomes secondary.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The opportunity isn&#39;t in building perfect AI agents. It&#39;s in building systems that help humans navigate imperfection — that make strong suggestions, explain their reasoning, accept feedback, and improve through iteration.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Progress doesn&#39;t come from eliminating uncertainty. It comes from designing systems that help people work with it.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Chasing deterministic AI agents may feel like building the future. But the real future belongs to systems that are interactive, collaborative, and intelligently imperfect.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;And that&#39;s not a limitation. That&#39;s the breakthrough.&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/h2&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/6505099001302217406'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/6505099001302217406'/><link rel='alternate' type='text/html' href='http://khangaonkar.blogspot.com/2026/04/why-deterministic-ai-agents-are-wrong.html' title='Why Deterministic AI Agents Are The Wrong Goal ?'/><author><name>Manoj Khangaonkar</name><uri>http://www.blogger.com/profile/13925926828045222969</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-5008017311510568944.post-9040895398183548724</id><published>2026-03-07T08:48:00.000-08:00</published><updated>2026-03-14T14:49:52.045-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Agentic AI"/><category scheme="http://www.blogger.com/atom/ns#" term="AI Agent"/><category scheme="http://www.blogger.com/atom/ns#" term="Artificial Intelligence"/><category scheme="http://www.blogger.com/atom/ns#" term="Large Language Models"/><category scheme="http://www.blogger.com/atom/ns#" term="LLM"/><title type='text'>AI Context Explained: The Real Engineering Behind Modern AI Systems</title><content type='html'>&lt;p&gt;Most discussions about large language models focus on prompts — how to phrase instructions to get better responses. But in real AI systems, prompts are only a small part of the story.&lt;/p&gt;&lt;p&gt;What actually determines the quality of an AI system is context: the information available to the model when it generates a response. This includes prompts, conversation history, retrieved documents, tool outputs, and sometimes structured application state. Designing how this information is assembled and provided to the model is what many engineers now call context engineering.&lt;/p&gt;&lt;p data-path-to-node=&quot;7&quot;&gt;Providing the right context to the LLM is the only reliable way to get accurate, production-grade answers. In this blog, I explore what context actually is, the hidden dangers of massive context windows, and how it should be used in Agentic AI.&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Context&lt;/h3&gt;&lt;div&gt;&lt;p data-path-to-node=&quot;9&quot;&gt;Context refers to all the information that is &lt;i data-index-in-node=&quot;46&quot; data-path-to-node=&quot;9&quot;&gt;not&lt;/i&gt; in the user&#39;s immediate question, but is required to help the LLM generate a relevant, highly specific answer. It is the data that gives the LLM situational awareness.&lt;/p&gt;&lt;p data-path-to-node=&quot;10&quot;&gt;Consider this basic prompt:&lt;/p&gt;&lt;blockquote data-path-to-node=&quot;11&quot;&gt;&lt;p data-path-to-node=&quot;11,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;11,0&quot;&gt;Prompt:&lt;/b&gt; &lt;i data-index-in-node=&quot;8&quot; data-path-to-node=&quot;11,0&quot;&gt;What is a good stock mutual fund to invest in?&lt;/i&gt;
&lt;b data-index-in-node=&quot;55&quot; data-path-to-node=&quot;11,0&quot;&gt;Response (Abbreviated):&lt;/b&gt; &lt;i data-index-in-node=&quot;79&quot; data-path-to-node=&quot;11,0&quot;&gt;1. T. Rowe Price Global Technology Fund (PRGTX) 2. Wasatch Ultra Growth Fund (WAMCX).&lt;/i&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p data-path-to-node=&quot;12&quot;&gt;For many investors, both of these are far too aggressive, high-risk, and expensive. Let&#39;s change the prompt slightly to inject some context:&lt;/p&gt;&lt;blockquote data-path-to-node=&quot;13&quot;&gt;&lt;p data-path-to-node=&quot;13,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;13,0&quot;&gt;Prompt:&lt;/b&gt; &lt;i data-index-in-node=&quot;8&quot; data-path-to-node=&quot;13,0&quot;&gt;What is a good stock mutual fund to invest in? I am 56 years old, nearing retirement. I prefer low-risk, low-cost, highly diversified funds.&lt;/i&gt;
&lt;b data-index-in-node=&quot;149&quot; data-path-to-node=&quot;13,0&quot;&gt;Response (Abbreviated):&lt;/b&gt; &lt;i data-index-in-node=&quot;173&quot; data-path-to-node=&quot;13,0&quot;&gt;1. Vanguard Target Retirement 2035 Fund (VTTHX) 2. Fidelity ZERO Total Market Index Fund (FZROX) 3. Vanguard Total Bond Market Index Fund (VBTLX).&lt;/i&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p data-path-to-node=&quot;14&quot;&gt;This response is drastically different and entirely appropriate for a conservative investor. The phrase &lt;i data-index-in-node=&quot;104&quot; data-path-to-node=&quot;14&quot;&gt;&quot;56 years old, nearing retirement. I prefer low-risk, low-cost, highly diversified funds&quot;&lt;/i&gt; is the &lt;b data-index-in-node=&quot;201&quot; data-path-to-node=&quot;14&quot;&gt;context&lt;/b&gt;. Without it, asking the LLM the same question multiple times will yield scattered, generic, or even dangerous financial advice.&lt;/p&gt;&lt;/div&gt;&lt;h3 data-path-to-node=&quot;15&quot;&gt;How is Context Passed to the Model?&lt;/h3&gt;&lt;p data-path-to-node=&quot;16&quot;&gt;Whether you use native provider APIs (OpenAI, Google) or orchestration frameworks like LangChain, context is not a separate magical parameter. &lt;b data-index-in-node=&quot;143&quot; data-path-to-node=&quot;16&quot;&gt;It is embedded directly into the input messages.&lt;/b&gt;&lt;/p&gt;&lt;p data-path-to-node=&quot;17&quot;&gt;A raw API call looks like this:&lt;/p&gt;&lt;div _ngcontent-ng-c134389950=&quot;&quot; class=&quot;code-block ng-tns-c134389950-224 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation&quot; data-hveid=&quot;0&quot; data-ved=&quot;0CAAQhtANahgKEwiK_r_KpsGSAxUAAAAAHQAAAAAQ2wg&quot; decode-data-ved=&quot;1&quot; jslog=&quot;223238;track:impression,attention;BardVeMetadataKey:[[&amp;quot;r_c55d4f56f422ca34&amp;quot;,&amp;quot;c_cafb054329b457d1&amp;quot;,null,&amp;quot;rc_4151960fa37c827b&amp;quot;,null,null,&amp;quot;en&amp;quot;,null,1,null,null,1,0]]&quot;&gt;&lt;div _ngcontent-ng-c134389950=&quot;&quot; class=&quot;code-block-decoration header-formatted gds-title-s ng-tns-c134389950-224 ng-star-inserted&quot;&gt;&lt;span _ngcontent-ng-c134389950=&quot;&quot; class=&quot;ng-tns-c134389950-224&quot;&gt;# Python&lt;/span&gt;&lt;div _ngcontent-ng-c134389950=&quot;&quot; class=&quot;buttons ng-tns-c134389950-224 ng-star-inserted&quot;&gt;&lt;!----&gt;&lt;!----&gt;&lt;/div&gt;&lt;!----&gt;&lt;/div&gt;&lt;!----&gt;&lt;div _ngcontent-ng-c134389950=&quot;&quot; class=&quot;formatted-code-block-internal-container ng-tns-c134389950-224&quot;&gt;&lt;div _ngcontent-ng-c134389950=&quot;&quot; class=&quot;animated-opacity ng-tns-c134389950-224&quot;&gt;&lt;pre _ngcontent-ng-c134389950=&quot;&quot; class=&quot;ng-tns-c134389950-224&quot;&gt;&lt;code _ngcontent-ng-c134389950=&quot;&quot; class=&quot;code-container formatted ng-tns-c134389950-224&quot; data-test-id=&quot;code-content&quot; role=&quot;text&quot;&gt;client.responses.create(
    model=&lt;span class=&quot;hljs-string&quot;&gt;&quot;gpt-5.2&quot;&lt;/span&gt;,
    messages=[ 
        {&lt;span class=&quot;hljs-string&quot;&gt;&quot;role&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;system&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&quot;content&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;You are a financial advisor.&quot;&lt;/span&gt;}, &lt;span class=&quot;hljs-comment&quot;&gt;# system prompt&lt;/span&gt;
        {&lt;span class=&quot;hljs-string&quot;&gt;&quot;role&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;user&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&quot;content&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;What is a good fund to invest in?&quot;&lt;/span&gt;} &lt;span class=&quot;hljs-comment&quot;&gt;# User prompt or query&lt;/span&gt;
&lt;span&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;&lt;/code&gt;{&lt;span class=&quot;hljs-string&quot;&gt;&quot;role&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;user&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&quot;content&quot;&lt;/span&gt;: &lt;span class=&quot;hljs-string&quot;&gt;&quot;I am 56 and prefer low risk.&quot;&lt;/span&gt;}, &lt;span class=&quot;hljs-comment&quot;&gt;# Context&lt;/span&gt;   &lt;/pre&gt;&lt;pre _ngcontent-ng-c134389950=&quot;&quot; class=&quot;ng-tns-c134389950-224&quot;&gt;&lt;code _ngcontent-ng-c134389950=&quot;&quot; class=&quot;code-container formatted ng-tns-c134389950-224&quot; data-test-id=&quot;code-content&quot; role=&quot;text&quot;&gt; ],
    temperature=&lt;span class=&quot;hljs-number&quot;&gt;0.0&lt;/span&gt;
)
&lt;/code&gt;&lt;/pre&gt;&lt;!----&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;p data-path-to-node=&quot;19&quot;&gt;Everything the LLM knows is stuffed into that &lt;code data-index-in-node=&quot;46&quot; data-path-to-node=&quot;19&quot;&gt;messages&lt;/code&gt; array. In an agentic system, it is all about getting the right information into that array at the right time.&lt;/p&gt;&lt;p data-path-to-node=&quot;20&quot;&gt;Context generally falls into three categories:&lt;/p&gt;&lt;p&gt;&lt;response-element ng-version=&quot;0.0.0-PLACEHOLDER&quot;&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;code-block _nghost-ng-c134389950=&quot;&quot; class=&quot;ng-tns-c134389950-224 ng-star-inserted&quot;&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;/code-block&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;/response-element&gt;&lt;/p&gt;&lt;h3 data-path-to-node=&quot;22&quot;&gt;&lt;/h3&gt;&lt;p&gt;&lt;/p&gt;&lt;ul data-path-to-node=&quot;21&quot;&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;21,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;21,0,0&quot;&gt;Static:&lt;/b&gt; Data that rarely changes (e.g., &quot;User is a male, NY Yankees fan, foodie&quot;).&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;21,1,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;21,1,0&quot;&gt;Dynamic:&lt;/b&gt; Data that evolves as the agent runs and interacts with tools (e.g., the results of a real-time stock price lookup).&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;21,2,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;21,2,0&quot;&gt;Long-Lived:&lt;/b&gt; Data that spans across multiple sessions or days (e.g., &quot;User already rejected the Vanguard recommendation yesterday&quot;).&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWHq49m9jQo5QQg15gsNct4GxuimaWv0MvVazk64gn6KTtcIv_RfMKtPDO9Z0yTvw51QnW4Oe8zoCEey8kQ8Su_bvHFqw6OTloy53zD8tbV558-5-1LjQSWp-Xfb6xXR_KlHPrqxqpQfVcOYgoOImnvq0L96nmq541y5hMD3WQRuP4aYl_FI1yG5mk_ms/s960/AI%20Context%20Assembly.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;720&quot; data-original-width=&quot;960&quot; height=&quot;308&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWHq49m9jQo5QQg15gsNct4GxuimaWv0MvVazk64gn6KTtcIv_RfMKtPDO9Z0yTvw51QnW4Oe8zoCEey8kQ8Su_bvHFqw6OTloy53zD8tbV558-5-1LjQSWp-Xfb6xXR_KlHPrqxqpQfVcOYgoOImnvq0L96nmq541y5hMD3WQRuP4aYl_FI1yG5mk_ms/w516-h308/AI%20Context%20Assembly.jpg&quot; width=&quot;516&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;In practice, building AI systems is often less about “prompt engineering” and more about deciding what information should be included in the model’s context at the moment of inference.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The data comes from a variety of sources. Cache would have the most recent data. The databases have the system of record. The log have events as they occurs. Context engineering involves getting the right data from the right place at the right time.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Note that context can go stale. If you feed in stale context, you get less the accurate answers. Keeping it current is part of the engineering.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;The Illusion of the Infinite Context Window&lt;/h3&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The context window refers to how much of your context the LLM retains for a conversation. The more it can remember, the better. Right ?&lt;/div&gt;&lt;p data-path-to-node=&quot;23&quot;&gt;LLM providers aggressively advertise their context window sizes. Bigger appears better, but that is a dangerous trap for developers.&lt;/p&gt;&lt;p data-path-to-node=&quot;24&quot;&gt;The context window simply represents the hard cap on how much text an LLM can &quot;see&quot; at once. Look at the landscape in early 2026:&lt;/p&gt;&lt;ul data-path-to-node=&quot;25&quot;&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;25,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;25,0,0&quot;&gt;Meta Llama 4 Scout:&lt;/b&gt; ~10 Million tokens&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;25,1,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;25,1,0&quot;&gt;Gemini 3 Pro:&lt;/b&gt; ~1.0M - 2.0M tokens&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;25,2,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;25,2,0&quot;&gt;OpenAI GPT-5.2:&lt;/b&gt; ~400,000 tokens&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;25,3,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;25,3,0&quot;&gt;Claude 4.5 Sonnet:&lt;/b&gt; ~1.0M tokens&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;25,4,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;25,4,0&quot;&gt;DeepSeek-R1:&lt;/b&gt; ~164,000 tokens&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p data-path-to-node=&quot;26&quot;&gt;However, the context window is a &lt;i data-index-in-node=&quot;33&quot; data-path-to-node=&quot;26&quot;&gt;model attribute&lt;/i&gt;, not an &lt;i data-index-in-node=&quot;57&quot; data-path-to-node=&quot;26&quot;&gt;agent capability&lt;/i&gt;. Research in 2025 and 2026 has consistently proven that models severely degrade well before hitting their upper limits. This phenomenon is known as &lt;b data-index-in-node=&quot;222&quot; data-path-to-node=&quot;26&quot;&gt;Context Rot&lt;/b&gt;.&lt;/p&gt;&lt;p data-path-to-node=&quot;27&quot;&gt;Just because a model &lt;i data-index-in-node=&quot;21&quot; data-path-to-node=&quot;27&quot;&gt;can&lt;/i&gt; accept 1 million tokens (about 8 full novels) doesn&#39;t mean it pays equal attention to all of them. Studies show that when a context window passes 50% capacity, models begin to heavily favor tokens at the very beginning or the very end of the prompt, completely ignoring critical constraints buried in the middle.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h3 data-path-to-node=&quot;29&quot;&gt;&lt;/h3&gt;&lt;p&gt;&lt;/p&gt;&lt;p data-path-to-node=&quot;28&quot;&gt;The industry is now focusing on the &lt;b data-index-in-node=&quot;36&quot; data-path-to-node=&quot;28&quot;&gt;Maximum Effective Context Window (MECW)&lt;/b&gt;. A model might advertise 1 million tokens, but its MECW—the point where accuracy actually drops off a cliff—might be only 130k tokens.&lt;/p&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;h3 data-path-to-node=&quot;29&quot;&gt;The Agent Loop&lt;/h3&gt;&lt;p data-path-to-node=&quot;30&quot;&gt;Because of Context Rot, you cannot just dump an entire database into the LLM and expect it to figure things out. This is why we build &lt;b data-index-in-node=&quot;134&quot; data-path-to-node=&quot;30&quot;&gt;Agents&lt;/b&gt;.&lt;/p&gt;&lt;p data-path-to-node=&quot;31&quot;&gt;An LLM is a stateless text predictor. An Agent is a software loop that uses the LLM as a reasoning engine to manage its own context. Agents operate in a continuous cycle: &lt;b data-index-in-node=&quot;171&quot; data-path-to-node=&quot;31&quot;&gt;Observe → Think → Act&lt;/b&gt;.&lt;/p&gt;&lt;p data-path-to-node=&quot;32&quot;&gt;Imagine building an AI-based investment analysis product. The agent doesn&#39;t just ask the LLM one massive question. It loops:&lt;/p&gt;&lt;ol data-path-to-node=&quot;33&quot; start=&quot;1&quot;&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;33,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;33,0,0&quot;&gt;Observe:&lt;/b&gt; The user asks, &lt;i data-index-in-node=&quot;24&quot; data-path-to-node=&quot;33,0,0&quot;&gt;&quot;Should I adjust my portfolio for the upcoming rate cuts?&quot;&lt;/i&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;33,1,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;33,1,0&quot;&gt;Think (LLM):&lt;/b&gt; The model realizes it lacks context. It outputs a tool-call: &lt;code data-index-in-node=&quot;74&quot; data-path-to-node=&quot;33,1,0&quot;&gt;get_user_portfolio()&lt;/code&gt; and &lt;code data-index-in-node=&quot;99&quot; data-path-to-node=&quot;33,1,0&quot;&gt;get_risk_tolerance()&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;33,2,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;33,2,0&quot;&gt;Act (Code):&lt;/b&gt; Agent code queries a PostgreSQL database to fetch the financial profile.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;33,3,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;33,3,0&quot;&gt;Update Context:&lt;/b&gt; It appends appends only the relevant portfolio metrics into the &lt;code data-index-in-node=&quot;83&quot; data-path-to-node=&quot;33,3,0&quot;&gt;messages&lt;/code&gt; array.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;33,4,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;33,4,0&quot;&gt;Loop:&lt;/b&gt; The agent sends this newly enriched, highly specific context back to the LLM to generate the final advice.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p data-path-to-node=&quot;34&quot;&gt;In this loop, the context is actively mutating. The agent is continuously pruning the &lt;code data-index-in-node=&quot;86&quot; data-path-to-node=&quot;34&quot;&gt;messages&lt;/code&gt; array, summarizing old turns, and injecting fresh tool outputs to keep the token count well within the Maximum Effective Context Window.&lt;/p&gt;&lt;h3 data-path-to-node=&quot;35&quot;&gt;&lt;/h3&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;h3 data-path-to-node=&quot;35&quot;&gt;Prompt Engineering vs. Context Engineering&lt;/h3&gt;&lt;p data-path-to-node=&quot;36&quot;&gt;If prompt engineering is about &lt;i data-index-in-node=&quot;31&quot; data-path-to-node=&quot;36&quot;&gt;how you ask&lt;/i&gt; the question, context engineering is about &lt;i data-index-in-node=&quot;86&quot; data-path-to-node=&quot;36&quot;&gt;giving the model a little more data&lt;/i&gt;&amp;nbsp;before it attempts to answer.&lt;/p&gt;&lt;p data-path-to-node=&quot;37&quot;&gt;To use an operating system analogy: The LLM is the CPU, the Prompt is the executable command, and the Context Window is the RAM and context is the data in RAM.&lt;/p&gt;&lt;ul data-path-to-node=&quot;38&quot;&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;38,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;38,0,0&quot;&gt;Prompt Engineering&lt;/b&gt; is writing a better command. It is user-facing, static, and brittle.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p data-path-to-node=&quot;38,1,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;38,1,0&quot;&gt;Context Engineering&lt;/b&gt; is managing the data in RAM. It is developer-facing, dynamic, and systemic.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p data-path-to-node=&quot;39&quot;&gt;As we move toward enterprise-grade AI, prompts are no longer enough. Context engineering involves building the infrastructure that feeds the model. It encompasses Retrieval-Augmented Generation (RAG) to find specific documents, Episodic Memory Graphs to track user decisions over time, and Context Pruning to prevent token overflow.&lt;/p&gt;&lt;p style=&quot;font-family: &amp;quot;Google Sans&amp;quot;, sans-serif; line-height: 1.15; margin-top: 0px; text-align: left;&quot;&gt;&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;&lt;span data-index-in-node=&quot;0&quot; data-path-to-node=&quot;40&quot;&gt;The Frontier: Context Graphs&lt;/span&gt;&lt;/h3&gt;&lt;p&gt;&lt;/p&gt;&lt;p style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;span face=&quot;Google Sans, sans-serif&quot;&gt;While context engineering today is mostly about managing lists of messages, the future of enterprise AI lies in Context Graphs.&amp;nbsp;&lt;/span&gt;&lt;span face=&quot;&amp;quot;Google Sans&amp;quot;, sans-serif&quot;&gt;Current LLM context is linear—a flat, chronological scroll of &quot;User said X, Agent did Y.&quot; This works for chat, but it fails for complex enterprise workflows. Real-world business data isn&#39;t a timeline; it&#39;s a web of relationships.&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;span face=&quot;&amp;quot;Google Sans&amp;quot;, sans-serif&quot;&gt;Enter the Context Graph. Instead of dumping raw logs into the window, advanced agents now build and maintain a dynamic graph structure.&amp;nbsp;&lt;/span&gt;&lt;span face=&quot;&amp;quot;Google Sans&amp;quot;, sans-serif&quot;&gt;Nodes represent entities (User, File, Decision, Error).&amp;nbsp;&lt;/span&gt;&lt;span face=&quot;&amp;quot;Google Sans&amp;quot;, sans-serif&quot;&gt;Edges represent causality or relationships (e.g., User Upload caused Error 500, which triggered Retry Logic).&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;span face=&quot;&amp;quot;Google Sans&amp;quot;, sans-serif&quot;&gt;This structure transforms the context from a &quot;temporary scratchpad&quot; into an organizational brain. If a human auditor later asks, &quot;Why did the agent reject this loan application?&quot;, a linear log forces the LLM to re-read thousands of lines of text to guess the reason. A Context Graph simply traverses the edge: Loan Application -&amp;gt; {rejected\_because}}&amp;nbsp; -&amp;gt; Risk Score &amp;gt; 80.&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;line-height: 1.15; margin-top: 0px;&quot;&gt;&lt;span face=&quot;&amp;quot;Google Sans&amp;quot;, sans-serif&quot;&gt;For enterprise applications, this is the missing link. It allows agents to reason across disconnected data points (e.g., linking a Slack message from Tuesday to a Code Commit on Friday) without needing a massive, expensive context window to hold all the noise in between.&lt;/span&gt;&lt;/p&gt;&lt;p style=&quot;font-family: &amp;quot;Google Sans&amp;quot;, sans-serif; line-height: 1.15; margin-top: 0px; text-align: left;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;40&quot;&gt;Conclusion&lt;/b&gt;&lt;/p&gt;&lt;p data-path-to-node=&quot;40&quot;&gt;A perfectly engineered prompt might get you a clever answer once. But a well-engineered context pipeline ensures your Agent gets the accurate answer securely, cost-effectively, and consistently, every single time.&lt;/p&gt;&lt;p data-path-to-node=&quot;40&quot;&gt;Popular LLM models are advanced and sophisticated. But everyone has access to them. You have no advantage because you an LLM. Your advantage and intellectual property is in how you manage and feed context to the LLM. Though not new, how you collect, store and retrieve the data that forms the context is the real engineering.&amp;nbsp;&lt;/p&gt;&lt;p data-path-to-node=&quot;40&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/9040895398183548724'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/9040895398183548724'/><link rel='alternate' type='text/html' href='http://khangaonkar.blogspot.com/2026/03/what-is-ai-context-and-context.html' title='AI Context Explained: The Real Engineering Behind Modern AI Systems'/><author><name>Manoj Khangaonkar</name><uri>http://www.blogger.com/profile/13925926828045222969</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWHq49m9jQo5QQg15gsNct4GxuimaWv0MvVazk64gn6KTtcIv_RfMKtPDO9Z0yTvw51QnW4Oe8zoCEey8kQ8Su_bvHFqw6OTloy53zD8tbV558-5-1LjQSWp-Xfb6xXR_KlHPrqxqpQfVcOYgoOImnvq0L96nmq541y5hMD3WQRuP4aYl_FI1yG5mk_ms/s72-w516-h308-c/AI%20Context%20Assembly.jpg" height="72" width="72"/></entry><entry><id>tag:blogger.com,1999:blog-5008017311510568944.post-1983282178691491908</id><published>2026-02-09T10:41:00.000-08:00</published><updated>2026-02-14T15:10:26.236-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Agentic AI"/><category scheme="http://www.blogger.com/atom/ns#" term="AI"/><category scheme="http://www.blogger.com/atom/ns#" term="Artificial Intelligence"/><category scheme="http://www.blogger.com/atom/ns#" term="Large language Model"/><title type='text'>What is (Agentic) AI Memory ?</title><content type='html'>&lt;div style=&quot;text-align: left;&quot;&gt;I have seen a lot of posts on X and LinkedIn on the importance of Agentic AI memory.&amp;nbsp; What exactly is it ? Is it just another name for RAG ? Why is it different from any other application memory ? In this blog, I try to answer these questions.&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;&lt;i&gt;What do people mean when they say &quot;AI Memory&quot; ?&lt;/i&gt;&lt;/h3&gt;&lt;div&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;Most production LLM interactions rely on external memory systems. Everything called “memory” today is &lt;span data-end=&quot;604&quot; data-start=&quot;592&quot;&gt;mostly external&lt;/span&gt;.&lt;/div&gt;&lt;p data-end=&quot;605&quot; data-start=&quot;477&quot;&gt;At their core LLMs are stateless functions. You make a request with a prompt and some context data and it provides you with a response&lt;/p&gt;&lt;h3 data-end=&quot;652&quot; data-start=&quot;607&quot;&gt;&lt;span style=&quot;font-size: small; font-weight: normal;&quot;&gt;In real systems, AI memory usually means:&lt;/span&gt;&lt;/h3&gt;
&lt;p data-end=&quot;726&quot; data-start=&quot;655&quot; style=&quot;text-align: left;&quot;&gt;&lt;/p&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;Storing past interactions, user preferences, decisions, goals, or facts.&lt;/li&gt;&lt;li&gt;Retrieving &lt;em data-end=&quot;750&quot; data-start=&quot;740&quot;&gt;relevant&lt;/em&gt; parts later&lt;/li&gt;&lt;li&gt;Feeding a &lt;strong data-end=&quot;797&quot; data-start=&quot;775&quot;&gt;compressed version&lt;/strong&gt; back into the prompt&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;/p&gt;&lt;p data-end=&quot;841&quot; data-start=&quot;820&quot;&gt;So yes — at its core:&lt;/p&gt;&lt;blockquote data-end=&quot;907&quot; data-start=&quot;843&quot;&gt;
&lt;p data-end=&quot;907&quot; data-start=&quot;845&quot;&gt;&lt;strong data-end=&quot;907&quot; data-start=&quot;845&quot;&gt;Memory = save → retrieve → summarize → inject into context&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;&lt;p data-end=&quot;925&quot; data-start=&quot;909&quot;&gt;Nothing magical. But is that all ? seems just like a regular cache ? Read on.&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;&lt;i&gt;Is this just RAG (Retrieval Augmented Generation) ?&lt;/i&gt;&lt;/h3&gt;&lt;div&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;They are related but not the same.&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;font-size: small;&quot;&gt;&lt;i&gt;RAG (Retrieval Augmented Generation)&lt;/i&gt;&lt;/span&gt;&lt;/div&gt;&lt;p data-end=&quot;1039&quot; data-start=&quot;1031&quot;&gt;Purpose:&lt;/p&gt;&lt;p data-end=&quot;1039&quot; data-start=&quot;1031&quot;&gt;&lt;/p&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;Bring &lt;strong data-end=&quot;1070&quot; data-start=&quot;1048&quot;&gt;external knowledge&lt;/strong&gt; into the LLM&lt;/li&gt;&lt;li&gt;Docs, PDFs, financial data, code, policies&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;/p&gt;&lt;p data-end=&quot;1145&quot; data-start=&quot;1130&quot;&gt;Typical traits:&lt;/p&gt;&lt;ul data-end=&quot;1246&quot; data-start=&quot;1146&quot; style=&quot;text-align: left;&quot;&gt;&lt;li&gt;retrieval is stateless per query&lt;/li&gt;&lt;li&gt;Large text chunks&lt;/li&gt;&lt;li&gt;Query-driven retrieval&lt;/li&gt;&lt;li&gt;“What additional data can we provide to LLM&amp;nbsp; to help answer this question?”&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;&lt;i&gt;Agent / User Memory&lt;/i&gt;&lt;/h3&gt;&lt;div&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;Purpose:&lt;/div&gt;&lt;ul data-end=&quot;1373&quot; data-start=&quot;1281&quot; style=&quot;text-align: left;&quot;&gt;&lt;li&gt;Maintain &lt;strong data-end=&quot;1306&quot; data-start=&quot;1292&quot;&gt;continuity&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;Personalization&lt;/li&gt;&lt;li&gt;Learning user intent and preferences over time&lt;/li&gt;
&lt;/ul&gt;&lt;p data-end=&quot;1390&quot; data-start=&quot;1375&quot;&gt;Typical traits:&lt;/p&gt;&lt;ul data-end=&quot;1494&quot; data-start=&quot;1391&quot; style=&quot;text-align: left;&quot;&gt;&lt;li&gt;Long-lived&lt;/li&gt;&lt;li&gt;Highly structured&lt;/li&gt;&lt;li&gt;Small, distilled facts&lt;/li&gt;&lt;li&gt;“What can I provide to LLM so it remembers this user?”&lt;/li&gt;
&lt;/ul&gt;&lt;p data-end=&quot;1517&quot; data-start=&quot;1496&quot;&gt;Think of it this way:&lt;/p&gt;&lt;p data-end=&quot;1517&quot; data-start=&quot;1496&quot;&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi36uzgyoxqYesyhtWhGsLGH6EqDSys-CpAA6jK9Cwo9iqlhKJTY4wy8CAAKpXE5YwmRsGPMk0u0fivQ3G5nLRA-bCeaw41z8HjK9XUa6EbyIcl9xwEggeZN_LpMQimlmBqz7GTohTZ0Wlq8yFxvdv9Gksg-ylUJ-zQK22buJxF1U2C9hUForAMAJ7Olgg/s731/Screenshot%202026-02-13%20at%203.12.41%E2%80%AFPM.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;244&quot; data-original-width=&quot;731&quot; height=&quot;234&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi36uzgyoxqYesyhtWhGsLGH6EqDSys-CpAA6jK9Cwo9iqlhKJTY4wy8CAAKpXE5YwmRsGPMk0u0fivQ3G5nLRA-bCeaw41z8HjK9XUa6EbyIcl9xwEggeZN_LpMQimlmBqz7GTohTZ0Wlq8yFxvdv9Gksg-ylUJ-zQK22buJxF1U2C9hUForAMAJ7Olgg/w581-h234/Screenshot%202026-02-13%20at%203.12.41%E2%80%AFPM.png&quot; width=&quot;581&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;p data-end=&quot;1847&quot; data-start=&quot;1771&quot;&gt;They often use&amp;nbsp;can use&amp;nbsp;&lt;span data-end=&quot;1814&quot; data-start=&quot;1786&quot;&gt;the same retrieval tools&lt;/span&gt;, but they serve different roles.&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;&lt;i&gt;&lt;span style=&quot;font-size: medium;&quot;&gt;Where is the memory ?&lt;/span&gt;&lt;/i&gt;&lt;/h3&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;i&gt;&lt;span style=&quot;font-size: medium;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/i&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;b&gt;Option 1: Agent process memory&lt;/b&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;Any suitable data structure like a HashMap.&lt;br /&gt;Suitable for cases where the Agent loop is short and no persistence is needed.&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;b&gt;Option 2: Redis /Cache&lt;/b&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;Suitable for session info, recent conversation history, tool results cache, temporary state.&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;.&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;b&gt;Option 3: PostgreSQL/RDBMS&lt;/b&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;Suitable when you need durability, auditability, explainability.&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;b&gt;Option 4: Vector databases&lt;/b&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;Suitable for semantic search.&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;b&gt;Option 5: AI memory tools&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Such as LangGraph memory, LlamaIndex memory, Memgpt. They try to make it easier for agents to store and retrieve.&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;Here is example of data that might be stored in memory:&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;div&gt;{&lt;/div&gt;&lt;div&gt;&amp;nbsp; &quot;user_id&quot;: &quot;123&quot;,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &quot;fact&quot;: &quot;User prefers concise python code&quot;,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &quot;source&quot;: &quot;conversation_turn_5&quot;,&lt;/div&gt;&lt;div&gt;&amp;nbsp; &quot;timestamp&quot;: &quot;2026-02-09&quot;&lt;/div&gt;&lt;div&gt;}&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;&lt;i&gt;The mental model for AI memory&lt;/i&gt;&lt;/h3&gt;&lt;div&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;Short term memory&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This is about recent interactions. It is data relevant to the current topic being discussed. For example, the user prefers conservative answers.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Long term memory&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This is stored externally, perhaps even to persistent storage. It is retrieved and inserted into context selectively. For example, the user is a vegetarian or the user&#39;s risk tolerance is low.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;&lt;i&gt;Memory and the LLM&lt;/i&gt;&lt;/h3&gt;&lt;div&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;The LLM takes as input only messages. Agent has to read the data from memory and insert it into the text message. This is what they refer to as context.&lt;br /&gt;&lt;br /&gt;You do not want add large amount of arbitrary data as context because:&lt;br /&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;text is converted to token and token cost spirals&lt;/li&gt;&lt;li&gt;LLM attention degrades with noise&lt;/li&gt;&lt;li&gt;Latency increases&lt;/li&gt;&lt;li&gt;Reasoning quality declines&lt;/li&gt;&lt;/ul&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;&lt;i&gt;Real Agentic Memory&lt;/i&gt;&lt;/h3&gt;&lt;div&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;At the start of the blog, I asked &quot;is this just a regular cache ?&quot;.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;To be useful in the agentic way, what is stored in the memory needs to evolve. Older or maybe irrelevant data in the memory needed to be &quot;forgotten&quot; or evicted based on intelligence (not standard algorithms like FIFO, LIFO etc). Updates and evictions need to happen based on recent interactions. If the historical information is too long and should not be evicted, it might need to be compressed.&lt;br /&gt;&lt;br /&gt;Agentic systems require more dynamic memory evolution than typical CRUD applications. In the case of long running agents, the quality of data in the memory has to get better with interactions over time.&lt;br /&gt;&lt;br /&gt;How exactly that can be implemented is beyond the scope of this blog and could be a topic for a future one.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;&lt;i&gt;Considerations&lt;/i&gt;&lt;/h3&gt;&lt;/div&gt;&lt;div&gt;&lt;i&gt;&lt;br /&gt;&lt;/i&gt;&lt;/div&gt;&lt;div&gt;Memory != Raw History&lt;/div&gt;&lt;div&gt;Bad Use : Here are the last 47 conversations ......&lt;/div&gt;&lt;div&gt;Better Use : We were talking about my retirement goals with this income and number of years to retire.&lt;br /&gt;&lt;br /&gt;Summarize and abstract to extract intelligence - as opposed to dumping large quantity of data.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;&lt;i&gt;In conclusion&lt;/i&gt;&lt;/h3&gt;&lt;div&gt;AI memory is structured state, sometimes summarized that is retrieved when needed and included as LLM input as &quot;context&quot;.&lt;/div&gt;&lt;p data-end=&quot;2586&quot; data-start=&quot;2578&quot;&gt;&lt;br /&gt;Conceptually, it is similar to RAG but they apply to different use cases.&lt;br /&gt;&lt;br /&gt;Better and smaller contexts beat large contexts and large memory.&lt;/p&gt;&lt;p data-end=&quot;2586&quot; data-start=&quot;2578&quot;&gt;Agentic AI Memory adds value only when&lt;br /&gt;&lt;/p&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;The system changes behavior ( for the better ) because of it&lt;/li&gt;&lt;li&gt;It produces better response, explanations, reasonings&lt;/li&gt;&lt;li&gt;It saves time&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;p data-end=&quot;1267&quot; data-start=&quot;936&quot;&gt;These ideas are not purely theoretical. While building Vestra — an AI agent focused on personal financial planning and modeling — I’ve had to think deeply about what &lt;em data-end=&quot;1096&quot; data-start=&quot;1088&quot;&gt;should&lt;/em&gt; be remembered, what should be abstracted, and what should be discarded. In financial reasoning especially, raw history is far less useful than structured, evolving state.&lt;/p&gt;
&lt;p data-end=&quot;1407&quot; data-start=&quot;1269&quot;&gt;But yes, Agentic memory will be different than what we know as memory in regular apps — in the ways it is updated, evicted, and retrieved.&lt;/p&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;h2 data-end=&quot;3402&quot; data-start=&quot;3348&quot;&gt;&lt;/h2&gt;&lt;h2 data-end=&quot;4489&quot; data-start=&quot;4443&quot;&gt;&lt;/h2&gt;&lt;h2 data-end=&quot;4872&quot; data-start=&quot;4820&quot;&gt;&lt;p data-end=&quot;387&quot; data-start=&quot;125&quot; style=&quot;font-size: medium; font-weight: 400;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/h2&gt;&lt;br /&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/1983282178691491908'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/1983282178691491908'/><link rel='alternate' type='text/html' href='http://khangaonkar.blogspot.com/2026/02/what-is-agentic-ai-memory.html' title='What is (Agentic) AI Memory ?'/><author><name>Manoj Khangaonkar</name><uri>http://www.blogger.com/profile/13925926828045222969</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi36uzgyoxqYesyhtWhGsLGH6EqDSys-CpAA6jK9Cwo9iqlhKJTY4wy8CAAKpXE5YwmRsGPMk0u0fivQ3G5nLRA-bCeaw41z8HjK9XUa6EbyIcl9xwEggeZN_LpMQimlmBqz7GTohTZ0Wlq8yFxvdv9Gksg-ylUJ-zQK22buJxF1U2C9hUForAMAJ7Olgg/s72-w581-h234-c/Screenshot%202026-02-13%20at%203.12.41%E2%80%AFPM.png" height="72" width="72"/></entry><entry><id>tag:blogger.com,1999:blog-5008017311510568944.post-1223835373673069259</id><published>2026-01-15T12:27:00.000-08:00</published><updated>2026-01-15T12:31:12.692-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="distributed applications"/><category scheme="http://www.blogger.com/atom/ns#" term="Distributed systems"/><category scheme="http://www.blogger.com/atom/ns#" term="System Design"/><title type='text'>Unique ID generation</title><content type='html'>&lt;p&gt;Unique ID generation is a deceptively simple task in application development. While it can seem trivial in a monolithic system, getting it right in a distributed system presents a few challenges. In this blog, I explore the commonly used ID generation techniques, where they fail and how to improve on them.&amp;nbsp;&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Why are unique Ids needed in applications ?&lt;/h3&gt;&lt;p&gt;They are needed to prevent duplicates. Say the system creates users or products or any other entity. You could use name at the key for uniqueness constraint. But there could be two users or products with the same name. For users, email is an option but that might not be available for other kinds of entities.&lt;br /&gt;&lt;br /&gt;Sometimes you are consuming messages from other systems over platforms like Kafka. Processing the same message multiple times can lead to errors. Duplicate delivery can happen for a variety of reasons that can be out of control of the consumer. Senders therefore include a uniqueId with the message so that consumer can ignore ids already processed (Idempotency) .&lt;/p&gt;&lt;p&gt;They can be useful for ordering. Ordering is useful to determine which event happened before others. It can be useful if you want to query for the most recent events or ignore older events.&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;What should the size and form of an id be ?&amp;nbsp;&lt;/h3&gt;&lt;p&gt;Should it be a number or should it be alphanumeric? Should it be&amp;nbsp; 32 bit, 64 bit or larger.&lt;/p&gt;&lt;p&gt;With 32 bit the maximum numeric id you can generate is ~ 4 billion. Sufficient for most systems but not enough if your product is internet scale. With 64 bit, you can generate id into the hundreds of trillions.&amp;nbsp;&lt;/p&gt;&lt;p&gt;But size is not the only issue. It is not the case that ids will generated in sequence in one place one after the other. In any system of even moderate scale, the unique ids will need to be generated from multiple nodes.&amp;nbsp;&lt;/p&gt;&lt;p&gt;On the topic of form, numerics ids are generally preferred as the take up less storage and can be easily ordered and indexed.&lt;/p&gt;&lt;p&gt;In the rest of the blog, I go over some unique id generation techniques I have come across.&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;ID GenerationTechniques&lt;/h3&gt;&lt;h4 style=&quot;text-align: left;&quot;&gt;1. Auto increment feature of the database&lt;/h4&gt;&lt;p&gt;If your services uses a database, this becomes an obvious choice as every database supports auto increment.&lt;/p&gt;&lt;p&gt;With postgresql, you would set up the table as&lt;/p&gt;&lt;p&gt;CREATE TABLE users (id SERIAL PRIMARY KEY,&amp;nbsp; name TEXT not null);&lt;br /&gt;&lt;br /&gt;With mysql,&lt;br /&gt;&lt;br /&gt;CREATE TABLE users (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255) not null);&lt;/p&gt;&lt;p&gt;Every insert into the table generates an id in the id column.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;INSERT INTO users(name) VALUES (&#39;JOHN&#39;) ;&lt;br /&gt;INSERT INTO users(name) VALUES (&#39;MARY&#39;) ;&lt;br /&gt;&lt;/p&gt;&lt;p class=&quot;p1&quot; style=&quot;font-family: Menlo; font-feature-settings: normal; font-kerning: auto; font-language-override: normal; font-optical-sizing: auto; font-size-adjust: none; font-size: 11px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-emoji: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;&quot;&gt;&lt;span class=&quot;s1&quot; style=&quot;font-variant-ligatures: no-common-ligatures;&quot;&gt;&lt;span class=&quot;Apple-converted-space&quot;&gt;&amp;nbsp;&lt;/span&gt;id&lt;span class=&quot;Apple-converted-space&quot;&gt;&amp;nbsp; &lt;/span&gt;| name&lt;span class=&quot;Apple-converted-space&quot;&gt;&amp;nbsp; &lt;/span&gt;|&lt;span class=&quot;Apple-converted-space&quot;&gt;&amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;p1&quot; style=&quot;font-family: Menlo; font-feature-settings: normal; font-kerning: auto; font-language-override: normal; font-optical-sizing: auto; font-size-adjust: none; font-size: 11px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-emoji: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;&quot;&gt;&lt;span class=&quot;s1&quot; style=&quot;font-variant-ligatures: no-common-ligatures;&quot;&gt;-----+---------&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;p1&quot; style=&quot;font-family: Menlo; font-feature-settings: normal; font-kerning: auto; font-language-override: normal; font-optical-sizing: auto; font-size-adjust: none; font-size: 11px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-emoji: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;&quot;&gt;&lt;span class=&quot;s1&quot; style=&quot;font-variant-ligatures: no-common-ligatures;&quot;&gt;&lt;span class=&quot;Apple-converted-space&quot;&gt;&amp;nbsp;&lt;/span&gt;1 | JOHN |&lt;/span&gt;&lt;/p&gt;&lt;p class=&quot;p1&quot; style=&quot;font-family: Menlo; font-feature-settings: normal; font-kerning: auto; font-language-override: normal; font-optical-sizing: auto; font-size-adjust: none; font-size: 11px; font-stretch: normal; font-variant-alternates: normal; font-variant-east-asian: normal; font-variant-emoji: normal; font-variant-numeric: normal; font-variant-position: normal; font-variation-settings: normal; line-height: normal; margin: 0px;&quot;&gt;&lt;span class=&quot;s1&quot; style=&quot;font-variant-ligatures: no-common-ligatures;&quot;&gt;&amp;nbsp;2 | MARY |&lt;/span&gt;&lt;/p&gt;&lt;p&gt;While this is simple to setup and use, it is appropriate for simple crud applications with low usage. The disadvantages are&lt;br /&gt;&lt;/p&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;It requires an insert to the db to get an id&lt;/li&gt;&lt;li&gt;If the insert fails for other reasons, you can have gaps&lt;/li&gt;&lt;li&gt;For distributed applications, it is a single point of failure&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;The single point of failure issue can be solved setting up multiple databases in a multi master arrangement as show below. In fact, here we are handing out ids in batches of hundred to reduce the load on the database.&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEink95x1Uz1YXWjdLkzp3CU5LymZ42C8DMVZ3PadQliIRSRXUd4pNwoNDkrgEdLIJsgxwE2Xqo-T0Y7Bs8RdT7g8CyoD-C1paHXeWF9VyeEpTnWgcQIsIW7GWdeshxrlx6waUUJgPe3PV_suwUHdfiEjALN1Ls4dMjUi_STgFHDatZSd30jac0PaJAGwFE/s960/Multi%20master%20id%20gen.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;720&quot; data-original-width=&quot;960&quot; height=&quot;324&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEink95x1Uz1YXWjdLkzp3CU5LymZ42C8DMVZ3PadQliIRSRXUd4pNwoNDkrgEdLIJsgxwE2Xqo-T0Y7Bs8RdT7g8CyoD-C1paHXeWF9VyeEpTnWgcQIsIW7GWdeshxrlx6waUUJgPe3PV_suwUHdfiEjALN1Ls4dMjUi_STgFHDatZSd30jac0PaJAGwFE/w374-h324/Multi%20master%20id%20gen.jpg&quot; width=&quot;374&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;h4 style=&quot;text-align: left;&quot;&gt;2. UUID&lt;/h4&gt;&lt;p&gt;These are 128 bit alpha numeric strings that are quite easy to generate and can be suitable for ids because they are unique. An example UUID is&amp;nbsp;&lt;span style=&quot;background-color: white; font-family: Consolas, &amp;quot;Bitstream Vera Sans Mono&amp;quot;, &amp;quot;Courier New&amp;quot;, Courier, monospace; font-size: 15px; font-weight: 700;&quot;&gt;e3d6d0e9-b99f-4704-b672-b7f3cdaf5618&lt;/span&gt;&lt;/p&gt;&lt;p&gt;The advantages of UUID are:&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;easy to generate. Just add UUID generator to the service.&lt;/li&gt;&lt;li&gt;low probability of collision&lt;/li&gt;&lt;li&gt;Each replica will generate unique id&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;Disadvantages:&lt;/div&gt;&lt;div&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;128 bits for each id will eventually take up space&lt;/li&gt;&lt;li&gt;No ordering. Ids are random&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;UUID v1 has a time component and could be sortable but is not unique enough. UUID v4 is completely random. UUID v7 has a 48 bit timestamp followed by 78 bits of random data. So if you are using UUID, you want to use v7 or higher.&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;h4 style=&quot;text-align: left;&quot;&gt;3. Flickr ticket server&lt;/h4&gt;&lt;p&gt;The &lt;a href=&quot;https://code.flickr.net/2010/02/08/ticket-servers-distributed-unique-primary-keys-on-the-cheap/&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;Flickr ticket server&lt;/a&gt;&amp;nbsp; is an improvement of the auto increment feature. In the example we have in 1, the id is tied to the users table. You get a new id only when you insert into the user table. if you needed unique ids for another entity, you would need to add autoincrement column to that table.&amp;nbsp; But what if&amp;nbsp; you needed unique ids in increasing order across tables or tables as would be the case in a distributed system ?&lt;/p&gt;&lt;p&gt;We could create a generic table&lt;/p&gt;&lt;p&gt;CREATE TABLE idgenerator (id SERIAL PRIMARY KEY);&lt;br /&gt;&lt;br /&gt;This can work but it would keep accumulating rows of ids which would also be store elsewhere.&lt;/p&gt;&lt;p&gt;What they did at flicker was this&lt;br /&gt;&lt;br /&gt;create table ticketserver (id int auto_increment primary key, stub char(1));&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;When they need an id , the do&lt;br /&gt;&lt;br /&gt;replace into tickerserver(stub) values (&#39;a&#39;) ;&lt;br /&gt;select LAST_INSERT_ID();&lt;br /&gt;&lt;br /&gt;This table always have only 1 row, because everytime you need an id, you are replacing the previous row. The above code is Mysql specific.&lt;br /&gt;&lt;br /&gt;For SQL that will work on postgresql you can do it little differently&lt;/p&gt;&lt;p&gt;create table ticketserver( stub char(1) primary key, id bigint notnull default 0)&lt;br /&gt;&amp;nbsp; &amp;nbsp;&lt;br /&gt;INSERT into ticketserver(stub,id)&lt;br /&gt;VALUES(&#39;a&#39;, COALESE((SELECT ID from ticketserver where stub = &#39;a&#39;),0)&lt;br /&gt;ON CONFLICT(stub) DO UPDATE&lt;br /&gt;&amp;nbsp; SET id = ticketserver.id + 1&lt;br /&gt;&amp;nbsp;returning id&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;&lt;h4 style=&quot;text-align: left;&quot;&gt;4. Snow flake twitter approach&lt;/h4&gt;&lt;p&gt;This &lt;a href=&quot;https://blog.x.com/engineering/en_us/a/2010/announcing-snowflake&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;Twitter&lt;/a&gt; approach that does not rely on the database or any third party service. It can be generated in code just following the specification.&lt;/p&gt;&lt;p&gt;It is a 64 bit id.&lt;/p&gt;&lt;p&gt;The left most bit is a sign bit for future use.&lt;br /&gt;The next 41 bits are used for the timestamp. Current time in ms since the epoch Nov 4 2010 1:42:54 UTC. But you can use any epoch.&lt;br /&gt;The next 5 bits are for datacenter id - that gives you 2 power 5 or 32 data centers.&lt;br /&gt;The next 5 bits are for machine id. 32 machines per datacenter.&lt;br /&gt;The last 12 bits are a sequence id - giving you 2 power 12 = 4096 ids per ms ( per datacenter per machine)&lt;/p&gt;&lt;br /&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgtkSFEg0Xk9JMMr-BBMEvjcHh1WSYzG6nBD9rjhXf15LRo3fNDjOwf4Jj5YsokdWcsYXsbNQ1kWGUiuTcBMGNlQ6T5iv5a7oV2OvCAsECFJki20JgMzf20z4aTEFPIGOyjULfUWybEUJxFZDxRqbOAUqZdqEEBRyXBN9ZQm5l02BzeSy0X1qfKv2x9ijo/s960/Twitter%20Snowflake%20ID%20(1).jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em; text-align: center;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;720&quot; data-original-width=&quot;960&quot; height=&quot;155&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgtkSFEg0Xk9JMMr-BBMEvjcHh1WSYzG6nBD9rjhXf15LRo3fNDjOwf4Jj5YsokdWcsYXsbNQ1kWGUiuTcBMGNlQ6T5iv5a7oV2OvCAsECFJki20JgMzf20z4aTEFPIGOyjULfUWybEUJxFZDxRqbOAUqZdqEEBRyXBN9ZQm5l02BzeSy0X1qfKv2x9ijo/w431-h155/Twitter%20Snowflake%20ID%20(1).jpg&quot; width=&quot;431&quot; /&gt;&lt;/a&gt;&lt;p&gt;The value of 2 power 41 - 1 is 2,199,023,255,551. With 41 bits for timestamp , you get a lot ids. This will last almost 70 years from epoch.&lt;/p&gt;&lt;p&gt;You can change things to reduce the size from 64 bits if needed. You may not need a datacenter id or you can decide to use fewer bits for the timestamp.&lt;br /&gt;&lt;br /&gt;The advantages of this approach are&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;Decentralized generation. No dependency on DB. Lower latency&lt;/li&gt;&lt;li&gt;Time based ordering&lt;/li&gt;&lt;li&gt;Higher throughput&lt;/li&gt;&lt;li&gt;Datacenter id and machine id can help in debugging&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;The disadvantages are&lt;/div&gt;&lt;div&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;clocks need to be synchronized&lt;/li&gt;&lt;li&gt;datacenters/machines need unique id&lt;/li&gt;&lt;li&gt;epoch needs to be choose wisely&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Other considerations&lt;/h3&gt;&lt;div&gt;Clock drift: In distributed systems where timestamp is part of the generated ID, you need to be aware of clock drift and take steps to mitigate it.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Sequential vs Monotonic: Most the time IDs need to be only monotonic, that is always increasing. They not need to be sequential.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Batching: If strictly increasing is not a requirement and you are using the database, you can reduce the load on the database by having the database handout ids to services in batches ( eg. 100 at a time)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Summary&lt;/h3&gt;&lt;div&gt;&lt;br /&gt;ID generation evolves with your systems scale. When you are starting out, it is normal to keep things simple and go with auto increment. But sooner or later you will need to scale (good problem to have). But the Flicker and Twitter methods are solid. I personally like the Twitter approach as it has no dependency on the database. It offers an excellent balance of decentralization, ordering and efficiency but requires clock synchronization. Whatever approach you choose, you need to ensure that aligns with your systems consistency requirements, scaling needs and tolerance for operational complexity.&lt;/div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Reference&lt;/h3&gt;&lt;p&gt;1. &lt;a href=&quot;https://code.flickr.net/2010/02/08/ticket-servers-distributed-unique-primary-keys-on-the-cheap/&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;Flickr Ticket Server&lt;/a&gt;&lt;/p&gt;&lt;p&gt;2. &lt;a href=&quot;https://blog.x.com/engineering/en_us/a/2010/announcing-snowflake&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;Twitter Snowflake&lt;/a&gt;&lt;/p&gt;&lt;br /&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/1223835373673069259'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/1223835373673069259'/><link rel='alternate' type='text/html' href='http://khangaonkar.blogspot.com/2026/01/unique-id-generation.html' title='Unique ID generation'/><author><name>Manoj Khangaonkar</name><uri>http://www.blogger.com/profile/13925926828045222969</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEink95x1Uz1YXWjdLkzp3CU5LymZ42C8DMVZ3PadQliIRSRXUd4pNwoNDkrgEdLIJsgxwE2Xqo-T0Y7Bs8RdT7g8CyoD-C1paHXeWF9VyeEpTnWgcQIsIW7GWdeshxrlx6waUUJgPe3PV_suwUHdfiEjALN1Ls4dMjUi_STgFHDatZSd30jac0PaJAGwFE/s72-w374-h324-c/Multi%20master%20id%20gen.jpg" height="72" width="72"/></entry><entry><id>tag:blogger.com,1999:blog-5008017311510568944.post-3171494031407365013</id><published>2025-11-30T17:34:00.000-08:00</published><updated>2025-12-01T13:14:56.354-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Cache"/><category scheme="http://www.blogger.com/atom/ns#" term="Distributed systems"/><category scheme="http://www.blogger.com/atom/ns#" term="System Design"/><title type='text'>Review : Facebook Memcache Paper</title><content type='html'>&lt;h2 style=&quot;text-align: left;&quot;&gt;Introduction&lt;/h2&gt;&lt;p&gt;Facebook (Meta) application handles billions of requests per second. The responses are built using thousands of items that needs to retrieved at low latencies to ensure good user experience. Low latency is achieved by retrieving the items from a cache. Once their business grew, a single node cache like Memcached would obviously not be able to handle the load. This paper is about how they solved the problem by building a distributed cache on top of Memcached.&lt;/p&gt;&lt;p&gt;In 2025, some might say the paper is a little dated ( 2013). But I think it is still interesting for several reasons. It is one of the early papers from a time when cloud and distributed systems exploded. I see it in the same category as the&amp;nbsp;&lt;a href=&quot;https://www.allthingsdistributed.com/files/amazon-dynamo-sosp2007.pdf&quot;&gt;Dynamo paper&lt;/a&gt;&amp;nbsp;from Amazon. While better technology is available today, this paper teaches important concepts. More importantly the paper shows how to take technology available and make more out it. In this case, they took a single node cache and built a distributed cache on top of it.&lt;/p&gt;&lt;p&gt;This is my summary of the paper&amp;nbsp;&lt;a href=&quot;https://www.usenix.org/system/files/conference/nsdi13/nsdi13-final170_update.pdf&quot;&gt;Scaling Memcache at Facebook&lt;/a&gt;.&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Requirements at Facebook&lt;/h2&gt;&lt;p&gt;- Allow near real time communication&lt;br /&gt;- aggregate content on the fly&lt;br /&gt;- access and update popular shared content&lt;br /&gt;- scale to millions of user requests per second&lt;/p&gt;&lt;p&gt;The starting point was single node Memcached servers.&lt;br /&gt;The target was a general purpose memory distributed key value store - called Memcache, that would be used by a variety of application use cases.&lt;/p&gt;&lt;p&gt;In the paper and in this blog, Memcached refers to the popular open source in memory key value store and Memcache is the distributed cached that Facebook built.&lt;/p&gt;&lt;p&gt;Observations:&lt;/p&gt;&lt;p&gt;Read volumes are several orders of magnitude higher than write volumes.&lt;br /&gt;Data fetched from multiple sources - HDFS, MySql etc&lt;br /&gt;Memcached supports simple primitives - set, get, delete&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Details&lt;/h2&gt;&lt;div&gt;The diagram below shows the Memcache architecture.&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkMSdupGy385TC00x4H8mCJDFa6DTVAzFadxrT_HXZwicI5E2THOaipDMt26Mk1tS5I6J7EsKSPNi3vQLMYpfmMQRJ-OD4fOgbRqhxNrJGNa8lnsOp0qQ8DlbigvpvNs8kIaIGoa0trLVMe7fPo-c6Slx8CWvs4gkYmsLjwAAl0o6k2iHP7iuW9RzuepI/s960/Memcache%20(1).jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;720&quot; data-original-width=&quot;960&quot; height=&quot;414&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkMSdupGy385TC00x4H8mCJDFa6DTVAzFadxrT_HXZwicI5E2THOaipDMt26Mk1tS5I6J7EsKSPNi3vQLMYpfmMQRJ-OD4fOgbRqhxNrJGNa8lnsOp0qQ8DlbigvpvNs8kIaIGoa0trLVMe7fPo-c6Slx8CWvs4gkYmsLjwAAl0o6k2iHP7iuW9RzuepI/w563-h414/Memcache%20(1).jpg&quot; width=&quot;563&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;p&gt;Memcache is a demand filled look aside cache. There can be thousands of memcached servers within a memcache cluster.&lt;/p&gt;&lt;p&gt;When an application needs to read data, it tries to get it from memcache. If not found in Memcache, it gets the data from the original source and populates Memcache.&lt;/p&gt;&lt;p&gt;When the application needs to write data, it writes to original source and the key in Memcache is invalidated.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Wide fan out: When the front end servers scale, the backend cache needs to scale too.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Items are distributed across Memcached servers using consistent hashing.&lt;/p&gt;&lt;p&gt;To handle a request, front end server might need to get data from many Memcached servers.&lt;br /&gt;Front end servers use a Memcache client to talk to memcached servers. Client is either a library or a proxy called mcrouter. Memcached servers do not communicate with each other.&lt;/p&gt;&lt;p&gt;Invalidation of the Memcached key is done by code running on the storage. It is not done by the client.&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Communication&lt;/h3&gt;&lt;p&gt;UDP is used for get requests. (Surprised ? clearly an optimization). Direct from client in webserver to Memcached.&amp;nbsp; Dropped requests are treated as a cache miss.&lt;br /&gt;TCP via mcrouter used for set and delete requests. Using mcrouter helps manage connections to storage.&lt;br /&gt;Client implements flow control to limit load on backend components&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Leases&lt;/h3&gt;Leases were implemented to address stale sets and thundering herd. Stale sets are caused by updating the cache with invalid values. Requiring a lease lets the system check that update is valid.&lt;br /&gt;Thundering herd happens when there is heavy read write activity on the same key at the same time. By handing out leases only every so often say 10 sec, they slow things down.&lt;p&gt;&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Memcache pools&lt;/h3&gt;&lt;p&gt;This is a general purpose caching layer used by different applications, different workloads with different requirements. To avoid interference, the clusters servers are partitioned into pools. For example, a pool for keys that are accessed frequently and cannot tolerate cache miss. Another pool for infrequently accessed keys. Each pool can scaled separately depending on requirement.&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Failures&lt;/h3&gt;&lt;p&gt;For small failures, the requests are directed to a set dedicated backup servers called gutters. When a large number of servers in the cluster down, the entire cluster is considered offline and traffic is routed to another cluster.&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Topology&lt;/h3&gt;&lt;p&gt;A frontend cluster is a group of webservers with Memcache.&lt;br /&gt;A region is frontend cluster&amp;nbsp; plus storage.&lt;br /&gt;This keeps the failure domains small.&lt;br /&gt;Storage is the final source of truth. Use mcsql and mcrouter to invalidate cache.&lt;/p&gt;&lt;p&gt;When going across data centers and across geo regions, the storage master in one region replicates to replicas in other regions. On an update, when the Memcached needs to be invalidated by the storage, it is not a problem if the update is in the master region. But if the update is in the replica region, then the&amp;nbsp; read after write might read a stale data as the replica might not have caught up. In replica regions, markers are used to ensure that only data in sync with the master is read.&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Summary&lt;/h3&gt;&lt;p&gt;In summary, this paper show how Facebook took a single node Memcached and scaled it to its growing needs. No new fundamental theories are applied or discussed. But this demonstrated innovation and trade offs in engineering to scale and grow in product in production that needs to scale to meet user demand.&amp;nbsp;&lt;/p&gt;&lt;p&gt;Key point is that separation cache and storage allowed each to be scaled separately. Kept focus on monitoring, debugging and operational efficiency. Gradual rollout and rollback of features kept a running system running.&lt;br /&gt;&lt;br /&gt;Some might say that Memcache is a hack. But some loss in architectural purity is worth it -- if your users and business stay happy.&lt;/p&gt;&lt;p&gt;Memcache and Facebook were developed together with application and system programmers working together to evolve and scale the system. This does not happen when teams work in isolation.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/3171494031407365013'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/3171494031407365013'/><link rel='alternate' type='text/html' href='http://khangaonkar.blogspot.com/2025/11/review-facebook-memcache-paper.html' title='Review : Facebook Memcache Paper'/><author><name>Manoj Khangaonkar</name><uri>http://www.blogger.com/profile/13925926828045222969</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkMSdupGy385TC00x4H8mCJDFa6DTVAzFadxrT_HXZwicI5E2THOaipDMt26Mk1tS5I6J7EsKSPNi3vQLMYpfmMQRJ-OD4fOgbRqhxNrJGNa8lnsOp0qQ8DlbigvpvNs8kIaIGoa0trLVMe7fPo-c6Slx8CWvs4gkYmsLjwAAl0o6k2iHP7iuW9RzuepI/s72-w563-h414-c/Memcache%20(1).jpg" height="72" width="72"/></entry><entry><id>tag:blogger.com,1999:blog-5008017311510568944.post-4041268339708271336</id><published>2025-10-05T16:09:00.000-07:00</published><updated>2025-10-05T16:09:21.764-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Analytics"/><category scheme="http://www.blogger.com/atom/ns#" term="architecture"/><category scheme="http://www.blogger.com/atom/ns#" term="Data Storage"/><category scheme="http://www.blogger.com/atom/ns#" term="Machine learning"/><title type='text'>Data Storage For Analytics And AI</title><content type='html'>&lt;p style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;font-weight: normal;&quot;&gt;For a&amp;nbsp; small or medium sized company storing all the data in relational database like Postgresql or MySQL is sufficient.&amp;nbsp; Perhaps if analytics is needed they might also use a columnar store, more like a data warehouse.&lt;/span&gt;&lt;/p&gt;&lt;div&gt;&lt;span style=&quot;font-weight: normal;&quot;&gt;What if you have large amounts of unstructured data as well ? May be logs from your e-commerce site, emails, support logs etc. Those need to be queried, aggregated, summarized and reported as well.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;If your business grows to handle large volumes of unstructured data—maybe logs from your e-commerce site, emails, support tickets, images, or customer audio—storing everything in a single RDBMS becomes impossible. These new data types require specialized architectures designed for scale, flexibility, and advanced analytics (like Machine Learning and Generative AI).&lt;/p&gt;&lt;p&gt;Here is a guide to the key data storage paradigms you will encounter:&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;span style=&quot;font-weight: normal;&quot;&gt;This is a brief introduction to the options.&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;1. Relational Database Management Systems (RDBMS)&lt;/h3&gt;&lt;div&gt;&lt;span style=&quot;font-weight: normal;&quot;&gt;&lt;br /&gt;This needs no introduction.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;p style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;font-weight: normal;&quot;&gt;Primary Use Case:&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-weight: normal;&quot;&gt;Online Transaction Processing (OLTP).&amp;nbsp;&lt;/span&gt;Applications requiring fast, frequent reads and writes, and ACID compliance.&lt;/p&gt;&lt;/div&gt;&lt;p style=&quot;text-align: left;&quot;&gt;Data Structure: Data is modeled at normalized rows and columns. Explicit relationships are enforced using foreign keys. In most cases storage is implemented as a B+ tree.&lt;/p&gt;&lt;div&gt;Schema Approach: The schema must be defined and enforced &lt;i&gt;before&lt;/i&gt; data can be written.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Examples:&amp;nbsp;&lt;b&gt;PostgreSQL&lt;/b&gt; (Open Source), &lt;b&gt;MySQL&lt;/b&gt; (Open Source/Commercial), Oracle, Microsoft SQL Server.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;2. Data Warehouse (DW)&lt;/h3&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Primary Use Case: Online Analytical Processing (OLAP). Business Intelligence (BI), historical reporting, and generating complex aggregated reports across years of data.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Data Structure: Columnar data store. Data often denormalized into Star or Snowflake schemas to optimize large, analytical &lt;code&gt;JOIN&lt;/code&gt; queries.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Schema Approach:&amp;nbsp;&lt;b&gt;Schema-on-Write&lt;/b&gt;: Data is cleaned, transformed, and structured via ETL/ELT pipelines &lt;i&gt;before&lt;/i&gt; loading.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Examples:&amp;nbsp;&lt;b&gt;Snowflake&lt;/b&gt;, &lt;b&gt;Google BigQuery&lt;/b&gt;, Amazon Redshift, Apache Pinot, Apache Druid, ClickHouse&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;3. Data Lake&lt;/h3&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Primary Use Case: Storing all data (raw and processed) at massive scale for Data Science, Machine Learning (ML), and exploratory analytics.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Data Structure: Stores data in its &lt;b&gt;native, raw format&lt;/b&gt;—structured, semi-structured (JSON, XML), and &lt;b&gt;unstructured&lt;/b&gt; (logs, images, audio).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Schema Approach:&amp;nbsp;&lt;b&gt;Schema-on-Read&lt;/b&gt;: Structure is applied dynamically by the query engine &lt;i&gt;when&lt;/i&gt; the data is read. This offers maximum flexibility.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Examples:&amp;nbsp;&lt;b&gt;Amazon S3&lt;/b&gt; (storage), &lt;b&gt;Azure Data Lake Storage (ADLS)&lt;/b&gt;, Apache Hadoop, Delta Lake, Apache Hudi&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;4. Data Lakehouse&lt;/h3&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Primary Use Case: Unifying the scale and flexibility of a Data Lake with the reliability and performance of a Data Warehouse.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Data Structure: Hybrid: Stores all raw data in the lake but adds a metadata and transaction layer (e.g., Delta Lake) to enforce quality and provide table-like features.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Schema Approach: Hybrid: Allows &lt;b&gt;Schema-on-Read&lt;/b&gt; for raw ingestion while enforcing &lt;b&gt;Schema Enforcement&lt;/b&gt; and &lt;b&gt;ACID transactions&lt;/b&gt; for curated tables.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Example: Databricks, Apache Iceberg&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;5. NOSQL Database&lt;/h3&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Primary Use Case: High-volume, dynamic, operational use cases where schemas change frequently and extreme horizontal scaling is needed (e.g., user profiles, content management).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Data Structure: Varies (Document, Key-Value, Graph, Wide-Column). Data is often stored as flexible records or objects without strict relationships.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Schema Approach:&lt;b&gt;Schema-less&lt;/b&gt; or &lt;b&gt;Dynamic Schema&lt;/b&gt;: Structure can evolve on a per-document basis without downtime.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Example:&amp;nbsp;&lt;b&gt;MongoDB&lt;/b&gt; (Document), &lt;b&gt;Redis&lt;/b&gt; (Key-Value/Cache), Apache Cassandra (Wide-Column), Neo4j (Graph).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;6. Vector Database&lt;/h3&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Given the rise of LLMs and Generative AI, this is one more specialized option critical for working with unstructured data:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This is designed to store and index &lt;b&gt;vector embeddings&lt;/b&gt;—numerical representations of unstructured data (text, images, audio) created by AI models. They allow for &lt;b&gt;similarity search&lt;/b&gt; (finding &quot;like&quot; data) rather than exact keyword matches.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Primary Use Case:&amp;nbsp;&lt;b&gt;Retrieval-Augmented Generation (RAG)&lt;/b&gt;, semantic search, recommendation engines, and high-dimensional ML applications.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Example:&amp;nbsp;&lt;b&gt;Pinecone&lt;/b&gt; (Commercial), &lt;b&gt;Weaviate&lt;/b&gt; (Open Source/Commercial), Qdrant.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Summary&lt;/h3&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;All of these options, from the structured RDBMS to the fluid Vector DB, combine to form a modern enterprise data architecture.&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;selected&quot;&gt;In essence, the modern enterprise no longer relies on a single data storage solution.&lt;/span&gt;&lt;span class=&quot;selected&quot;&gt;&amp;nbsp;The journey usually starts&amp;nbsp; with the &lt;/span&gt;&lt;strong&gt;RDBMS&lt;/strong&gt;&lt;span class=&quot;selected&quot;&gt; for transactional integrity, moves to the &lt;/span&gt;&lt;strong&gt;Data Warehouse&lt;/strong&gt;&lt;span class=&quot;selected&quot;&gt; for structured BI, and expands into the &lt;/span&gt;&lt;strong&gt;Data Lake&lt;/strong&gt;&lt;span class=&quot;selected&quot;&gt; to capture all raw, unstructured data necessary for Machine Learning and discovery. &lt;br /&gt;&lt;br /&gt;The &lt;/span&gt;&lt;strong&gt;Data Lakehouse&lt;/strong&gt;&lt;span class=&quot;selected&quot;&gt; is the cutting-edge step, unifying these functions by bringing governance and performance directly to the lake.&amp;nbsp;&lt;/span&gt;&lt;strong&gt;Vector Databases&lt;/strong&gt;&lt;span class=&quot;selected&quot;&gt; bridge the gap between unstructured data and the world of Generative AI.&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class=&quot;selected&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class=&quot;selected&quot;&gt;Understanding the specialized role of each platform is the first and most critical step in designing a future-proof data strategy that extracts maximum value from every piece of information your business creates.&lt;br /&gt;&lt;br /&gt;Note that there is some overlap between the categories. For example Postgresql supports JSONB and vector storage, making it useful for some NoSql and AI use cases. Some products that started of as data lakes added features to become lakehouses.&amp;nbsp;&lt;/span&gt;&lt;/div&gt;&lt;h3&gt;&lt;span style=&quot;font-weight: normal;&quot;&gt;&lt;br /&gt;&lt;/span&gt;&lt;/h3&gt;&lt;h3&gt;&lt;br /&gt;&lt;/h3&gt;&lt;h3&gt;&lt;input-container _ngcontent-ng-c3085279446=&quot;&quot; _nghost-ng-c3256437674=&quot;&quot; class=&quot;input-gradient ng-tns-c3256437674-2 ng-tns-c3085279446-1 ui-improvements-phase-1 ng-star-inserted&quot;&gt;&lt;div _ngcontent-ng-c3256437674=&quot;&quot; class=&quot;input-area-container ng-tns-c3256437674-2 ng-trigger ng-trigger-inputAreaAnimation ng-star-inserted&quot;&gt;&lt;input-area-v2 _ngcontent-ng-c3256437674=&quot;&quot; _nghost-ng-c2488338351=&quot;&quot; class=&quot;ng-tns-c2488338351-6 ng-tns-c3256437674-2 ui-improvements-phase-1 with-toolbox-drawer input-box-shadow ng-star-inserted&quot;&gt;&lt;div _ngcontent-ng-c2488338351=&quot;&quot; class=&quot;input-area ng-tns-c2488338351-6 with-toolbox-drawer&quot; data-node-type=&quot;input-area&quot;&gt;&lt;div _ngcontent-ng-c2488338351=&quot;&quot; class=&quot;text-input-field ng-tns-c2488338351-6 with-toolbox-drawer height-expanded-past-single-line&quot; xapfileselectordropzone=&quot;&quot;&gt;&lt;div _ngcontent-ng-c2488338351=&quot;&quot; class=&quot;trailing-actions-wrapper ng-tns-c2488338351-6&quot;&gt;&lt;div _ngcontent-ng-c2488338351=&quot;&quot; class=&quot;input-buttons-wrapper-bottom ng-tns-c2488338351-6&quot;&gt;&lt;!----&gt;&lt;!----&gt;&lt;/div&gt;&lt;/div&gt;&lt;!----&gt;&lt;/div&gt;&lt;/div&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;/input-area-v2&gt;&lt;!----&gt;&lt;/div&gt;&lt;!----&gt;&lt;hallucination-disclaimer _ngcontent-ng-c3256437674=&quot;&quot; _nghost-ng-c3581211345=&quot;&quot; aligntext=&quot;center&quot; class=&quot;hallucination-disclaimer ng-tns-c3256437674-2&quot;&gt;&lt;div _ngcontent-ng-c3581211345=&quot;&quot; class=&quot;capabilities-disclaimer&quot; style=&quot;--align-text: center;&quot;&gt;&lt;!----&gt;&lt;!----&gt;&lt;p _ngcontent-ng-c3581211345=&quot;&quot; class=&quot;gds-label-m-alt desktop-spacing ng-star-inserted&quot; data-test-id=&quot;disclaimer&quot;&gt;&lt;/p&gt;&lt;/div&gt;&lt;/hallucination-disclaimer&gt;&lt;/input-container&gt;&lt;/h3&gt;&lt;div class=&quot;horizontal-scroll-wrapper&quot;&gt;&lt;div class=&quot;table-block-component&quot;&gt;&lt;response-element ng-version=&quot;0.0.0-PLACEHOLDER&quot;&gt;&lt;table-block _nghost-ng-c3901580009=&quot;&quot; class=&quot;ng-star-inserted&quot;&gt;&lt;div _ngcontent-ng-c3901580009=&quot;&quot; class=&quot;table-block has-export-button&quot;&gt;&lt;/div&gt;&lt;/table-block&gt;&lt;/response-element&gt;&lt;/div&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/4041268339708271336'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/4041268339708271336'/><link rel='alternate' type='text/html' href='http://khangaonkar.blogspot.com/2025/10/data-storage-for-analytics-and-ai.html' title='Data Storage For Analytics And AI'/><author><name>Manoj Khangaonkar</name><uri>http://www.blogger.com/profile/13925926828045222969</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-5008017311510568944.post-1188217171951657569</id><published>2025-09-13T19:09:00.000-07:00</published><updated>2025-09-15T07:02:50.495-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="AI"/><category scheme="http://www.blogger.com/atom/ns#" term="AI Agent"/><category scheme="http://www.blogger.com/atom/ns#" term="Artificial Intelligence"/><category scheme="http://www.blogger.com/atom/ns#" term="Large language Model"/><category scheme="http://www.blogger.com/atom/ns#" term="LLM"/><title type='text'> What Does Adding AI To Your Product Even Mean?</title><content type='html'>&lt;h2 style=&quot;text-align: left;&quot;&gt;Introduction&lt;/h2&gt;&lt;p&gt;I have been asked this question multiple times: My management sent out a directive to all teams to add AI to the product. But I have no idea what that means ?&lt;/p&gt;&lt;chat-window-content _ngcontent-ng-c1252703984=&quot;&quot; _nghost-ng-c2592684441=&quot;&quot; class=&quot;ng-tns-c1252703984-1 ui-improvements-phase-1 ng-star-inserted&quot;&gt;&lt;div _ngcontent-ng-c2592684441=&quot;&quot; class=&quot;chat-history-scroll-container&quot; id=&quot;chat-history&quot;&gt;&lt;infinite-scroller _ngcontent-ng-c2592684441=&quot;&quot; _nghost-ng-c2552112181=&quot;&quot; class=&quot;chat-history ng-star-inserted&quot; data-lhcontainer=&quot;1&quot; data-test-id=&quot;chat-history-container&quot; jslog=&quot;268632;track:impression,attention&quot;&gt;&lt;div _ngcontent-ng-c2592684441=&quot;&quot; class=&quot;conversation-container message-actions-hover-boundary ng-star-inserted&quot; id=&quot;1620cf49c4b70ee4&quot; style=&quot;min-height: 589px;&quot;&gt;&lt;model-response _ngcontent-ng-c2592684441=&quot;&quot; _nghost-ng-c3216799029=&quot;&quot; class=&quot;ng-star-inserted&quot;&gt;&lt;div _ngcontent-ng-c3216799029=&quot;&quot;&gt;&lt;response-container _ngcontent-ng-c3216799029=&quot;&quot; _nghost-ng-c3395851099=&quot;&quot; class=&quot;ng-tns-c3395851099-208 reduced-bottom-padding ng-star-inserted&quot; jslog=&quot;188576;track:impression,attention;BardVeMetadataKey:[[&amp;quot;r_1620cf49c4b70ee4&amp;quot;,&amp;quot;c_b88b406507aac9b9&amp;quot;,null,null,null,null,null,null,1,null,null,null,0]];mutable:true&quot;&gt;&lt;div _ngcontent-ng-c3395851099=&quot;&quot; class=&quot;response-container ng-tns-c3395851099-208 response-container-with-gpi ng-star-inserted response-container-has-multiple-responses&quot; jslog=&quot;173900;track:impression,attention&quot;&gt;&lt;div _ngcontent-ng-c3395851099=&quot;&quot; class=&quot;presented-response-container ng-tns-c3395851099-208&quot;&gt;&lt;div _ngcontent-ng-c3395851099=&quot;&quot; class=&quot;response-container-content ng-tns-c3395851099-208&quot;&gt;&lt;div _ngcontent-ng-c3216799029=&quot;&quot; class=&quot;response-content ng-tns-c3395851099-208&quot;&gt;&lt;message-content _ngcontent-ng-c3216799029=&quot;&quot; _nghost-ng-c1405140501=&quot;&quot; class=&quot;model-response-text ng-star-inserted&quot; id=&quot;message-content-id-r_1620cf49c4b70ee4&quot; style=&quot;height: auto;&quot;&gt;&lt;div _ngcontent-ng-c1405140501=&quot;&quot; class=&quot;markdown markdown-main-panel tutor-markdown-rendering enable-updated-hr-color&quot; dir=&quot;ltr&quot; id=&quot;model-response-message-contentr_1620cf49c4b70ee4&quot; style=&quot;--animation-duration: 400ms; --fade-animation-function: linear;&quot;&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiVGlLi87qfbIy8X2j-uS8u3vIyirOaXfwYETd2bFXDiUVWy9yKS8brJHy0WgPeki5b9bHP_W09dncCZUHEkjRblba2lgtRw6OBHU1dGa-Gk-G39iO-kBdai3yaOmJJPmSC_gQ9U6SMCfPok0BWZ3g-tYUtTwHqbJL8Sr3ID1HhId1iY4cXu0FKh0aVJyo/s960/AI%20enterprise.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;720&quot; data-original-width=&quot;960&quot; height=&quot;284&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiVGlLi87qfbIy8X2j-uS8u3vIyirOaXfwYETd2bFXDiUVWy9yKS8brJHy0WgPeki5b9bHP_W09dncCZUHEkjRblba2lgtRw6OBHU1dGa-Gk-G39iO-kBdai3yaOmJJPmSC_gQ9U6SMCfPok0BWZ3g-tYUtTwHqbJL8Sr3ID1HhId1iY4cXu0FKh0aVJyo/w448-h284/AI%20enterprise.jpg&quot; width=&quot;448&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;In this blog I discuss what adding AI actually entails, moving beyond the hype to practical applications and what are some things you might try.&lt;/p&gt;&lt;p&gt;At its core, &lt;b&gt;adding AI to a product means &lt;/b&gt;using an AI model, either the more popular large language model (LLM) or a traditional ML model to either&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;predict answers&amp;nbsp;&lt;/li&gt;&lt;li&gt;generate new data - text, image , audio etc&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;The effect of that is it enable the product to&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;do a better job of responding to queries&lt;/li&gt;&lt;li&gt;automate repetitive tasks&lt;/li&gt;&lt;li&gt;personalize responses&lt;/li&gt;&lt;li&gt;extract insights&lt;/li&gt;&lt;li&gt;Reduce manual labor&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;It&#39;s about making your product smarter, more efficient, and more valuable by giving it capabilities it didn&#39;t have before.&lt;br /&gt;&lt;br /&gt;Any domain where there is a huge domain of published knowledge (programming, healthcare) or vast quantities of data (e-commerce, financial services, health, manufacturing etc), too large for the human brain to comprehend, AI has a place and will outperform what we currently do.&lt;/p&gt;&lt;hr /&gt;&lt;p&gt;&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;So how do you go about adding AI ?&lt;/h2&gt;&lt;div style=&quot;text-align: left;&quot;&gt;Thanks to social media, AI has developed the aura of being super-complicated. But if reality, if you use off the shelf models, it is not that hard. Training models is hard. But 97% of us, will never have to do it. Below is a simple 5 step approach to adding AI to your system.&lt;/div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;1. Requirements&lt;/h3&gt;&lt;div&gt;It is really important that you nail down the requirement before proceeding any further. What task is being automated ? What questions are you attempting to answer ?&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The AI solution will need to evaluated against this requirement. Not once or twice but on a continuous basis.&lt;/div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;2. Model&lt;/h3&gt;&lt;div&gt;Pick a model.&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;The recent explosion of interest in AI is largely due to &lt;b&gt;Large Language Models (LLMs)&lt;/b&gt; like ChatGPT. At its core, the LLM is a text prediction engine. Give it some text and it will give you text that likely to follow.&lt;br /&gt;&lt;br /&gt;But beyond text generation, LLMs have been been trained with a lot of published digital data and they retain associations between text. On top of it, they are trained with real world examples of questions and answers. For example, the reason they do such a good job at generating &quot;programming code&quot; is because they are trained with real source code from github repositories.&lt;br /&gt;&lt;br /&gt;What model to use ?&lt;/p&gt;&lt;p&gt;The choices are:&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;Commercial LLMs like ChatGpt, Claude, Gemini etc&lt;/li&gt;&lt;li&gt;Open source LLMs like Llama, Mistral, DeepSeek etc&lt;/li&gt;&lt;li&gt;Traditional ML models&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;Choosing the right model can make a difference to the results. There might be a model specially tuned for your problem domain.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Cost, latency and accuracy are some parameters that are used to evaluate models.&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;3. Agent&lt;/h3&gt;&lt;div&gt;Develop one or more agents.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Agent&lt;/b&gt; is the modern evolution of a service.&amp;nbsp; Agent is the glue that ties the AI model to the rest of your system.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The Agent is the orchestration layer that:&lt;/div&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;Accepts requests either from a UI or another service&lt;/li&gt;&lt;li&gt;Makes requests to the model on behalf of your system&lt;/li&gt;&lt;li&gt;Makes multiple API calls to&amp;nbsp; systems to fetch data&lt;/li&gt;&lt;li&gt;May search the internet&lt;/li&gt;&lt;li&gt;May save state to a database at various times&lt;/li&gt;&lt;li&gt;In the end, returns a response or start some process to finish a task&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;It is unlikely that you will develop a model. But it is very likely that you will develop one or more agents.&lt;/div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;4. Data pipeline&lt;/h3&gt;&lt;div&gt;Bring your data.&lt;/div&gt;&lt;p&gt;A generic AI model can only do so much. Even without additional training, just adding your data to the prompts can yield better results.&lt;/p&gt;&lt;p&gt;The data pipeline is what makes the data in your databases, logs, ticket systems, github, Jira etc available to the models and agents.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;get the data from source&lt;/li&gt;&lt;li&gt;clean it&lt;/li&gt;&lt;li&gt;format it&lt;/li&gt;&lt;li&gt;transform it&lt;/li&gt;&lt;li&gt;use it in either prompts or to further train the model&lt;/li&gt;&lt;/ul&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;5. Monitoring&lt;/h3&gt;&lt;div&gt;Monitor, tune, refine.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Lastly you need to continuously monitor results to ensure quality. LLMs are known to hallucinate and even drift. When the results are not good, your will try tweaking the prompt data, model parameters among other things.&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;Now let us seem how these concepts translate into some very simple real-world applications across different industries.&lt;/p&gt;&lt;hr /&gt;&lt;p&gt;&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Examples&lt;/h2&gt;&lt;h3&gt;1. Healthcare: Enhancing Diagnostics and Patient Experience&lt;/h3&gt;&lt;p&gt;Adding AI can mean:&lt;/p&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;b&gt;Personalized Treatment Pathways:&lt;/b&gt; An &lt;b&gt;AI Agent&lt;/b&gt; can analyze vast amounts of research papers, clinical trial data, and individual patient responses to suggest the most effective treatment plan tailored to a specific patient&#39;s profile.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;b&gt;Example:&lt;/b&gt; For a person with high cholesterol, an AI agent can come up with a personalized diet and exercise plan.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;hr /&gt;&lt;p&gt;&lt;/p&gt;&lt;h3&gt;2. Finance: Personalized Investing&lt;/h3&gt;&lt;p&gt;Adding AI could mean:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;b&gt;Personalized Financial Advice:&lt;/b&gt; Here, an &lt;b&gt;AI Agent&lt;/b&gt; can serve as a &quot;advisor&quot; to offer highly tailored investment portfolios and financial planning advice.&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;b&gt;Example:&lt;/b&gt; A banking app&#39;s &lt;b&gt;AI agent&lt;/b&gt; uses an LLM to understand your financial goals and then uses its &quot;tools&quot; to connect to your accounts, pull real-time market data, and recommend trades on your behalf. It can then use its LLM to explain in simple terms why it made a specific trade or rebalanced your portfolio.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;hr /&gt;&lt;p&gt;&lt;/p&gt;&lt;h3&gt;3. E-commerce: Customer Experience&lt;/h3&gt;&lt;p&gt;Adding AI could mean:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;b&gt;Personalized shopping:&lt;/b&gt; AI models can find the right product at the right price with the right characteristics for user requirement&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;b&gt;Example:&lt;/b&gt;&amp;nbsp;Instead of me shopping and comparing for hours, AI does it for me and makes a recommendation on the final product to purchase.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;hr /&gt;&lt;p&gt;&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;In Conclusion&lt;/h2&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;Adding AI to your product to make it better means using the proven power of AI models&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;To better answer customer request with insights&lt;/li&gt;&lt;li&gt;To automate repetitive time consuming task&lt;/li&gt;&lt;li&gt;To make predictions that were hard earlier&lt;/li&gt;&lt;li&gt;To gain insights into vast bodies of knowledge&amp;nbsp;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;The tools are there. But to get results you need discipline, patience and process.&lt;br /&gt;&lt;br /&gt;Start small. Focus on one specific business problem you want to solve, and build from there.&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;/div&gt;&lt;br /&gt;&lt;/message-content&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/response-container&gt;&lt;/div&gt;&lt;/model-response&gt;&lt;/div&gt;&lt;/infinite-scroller&gt;&lt;/div&gt;&lt;/chat-window-content&gt;&lt;input-container _ngcontent-ng-c1252703984=&quot;&quot; _nghost-ng-c1908767575=&quot;&quot; class=&quot;input-gradient ng-tns-c1908767575-2 ng-tns-c1252703984-1 ui-improvements-phase-1 ng-star-inserted&quot;&gt;&lt;div _ngcontent-ng-c1908767575=&quot;&quot; class=&quot;input-area-container ng-tns-c1908767575-2 ng-trigger ng-trigger-inputAreaAnimation ng-star-inserted&quot;&gt;&lt;input-area-v2 _ngcontent-ng-c1908767575=&quot;&quot; _nghost-ng-c2313549854=&quot;&quot; class=&quot;ng-tns-c2313549854-5 ng-tns-c1908767575-2 ui-improvements-phase-1 with-toolbox-drawer ng-star-inserted&quot;&gt;&lt;div _ngcontent-ng-c2313549854=&quot;&quot; class=&quot;input-area ng-tns-c2313549854-5 with-toolbox-drawer&quot; data-node-type=&quot;input-area&quot;&gt;&lt;div _ngcontent-ng-c2313549854=&quot;&quot; class=&quot;text-input-field ng-tns-c2313549854-5 with-toolbox-drawer height-expanded-past-single-line&quot; xapfileselectordropzone=&quot;&quot;&gt;&lt;div _ngcontent-ng-c2313549854=&quot;&quot; class=&quot;trailing-actions-wrapper ng-tns-c2313549854-5&quot;&gt;&lt;div _ngcontent-ng-c2313549854=&quot;&quot; class=&quot;input-buttons-wrapper-bottom ng-tns-c2313549854-5&quot;&gt;&lt;!----&gt;&lt;!----&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;!----&gt;&lt;/input-area-v2&gt;&lt;!----&gt;&lt;/div&gt;&lt;!----&gt;&lt;hallucination-disclaimer _ngcontent-ng-c1908767575=&quot;&quot; _nghost-ng-c664255260=&quot;&quot; aligntext=&quot;center&quot; class=&quot;halucination-disclaimer ng-tns-c1908767575-2&quot;&gt;&lt;div _ngcontent-ng-c664255260=&quot;&quot; class=&quot;capabilities-disclaimer&quot; style=&quot;--align-text: center;&quot;&gt;&lt;!----&gt;&lt;!----&gt;&lt;p _ngcontent-ng-c664255260=&quot;&quot; class=&quot;gds-label-m-alt desktop-spacing ng-star-inserted&quot; data-test-id=&quot;disclaimer&quot;&gt;&lt;/p&gt;&lt;/div&gt;&lt;/hallucination-disclaimer&gt;&lt;/input-container&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/1188217171951657569'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/1188217171951657569'/><link rel='alternate' type='text/html' href='http://khangaonkar.blogspot.com/2025/09/what-does-adding-ai-to-your-product.html' title=' What Does Adding AI To Your Product Even Mean?'/><author><name>Manoj Khangaonkar</name><uri>http://www.blogger.com/profile/13925926828045222969</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiVGlLi87qfbIy8X2j-uS8u3vIyirOaXfwYETd2bFXDiUVWy9yKS8brJHy0WgPeki5b9bHP_W09dncCZUHEkjRblba2lgtRw6OBHU1dGa-Gk-G39iO-kBdai3yaOmJJPmSC_gQ9U6SMCfPok0BWZ3g-tYUtTwHqbJL8Sr3ID1HhId1iY4cXu0FKh0aVJyo/s72-w448-h284-c/AI%20enterprise.jpg" height="72" width="72"/></entry><entry><id>tag:blogger.com,1999:blog-5008017311510568944.post-5702594110091639653</id><published>2025-09-06T06:50:00.000-07:00</published><updated>2025-09-06T06:50:25.972-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="distributed applications"/><category scheme="http://www.blogger.com/atom/ns#" term="Distributed systems"/><category scheme="http://www.blogger.com/atom/ns#" term="System Design"/><title type='text'>CRDT Tutorial: Conflict Free Replication Data Types</title><content type='html'>

&lt;p&gt;Have you ever wondered how Google docs, Figma, Notion provide real time collaborative editing?&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;The challenge is : What happens when 2 users edit the same part of the document at the same time.&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;User A at position 5: types X&lt;/li&gt;&lt;li&gt;User B at position 5: types Y&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;This is a concurrency problem. A traditional implementation would need to lock the document to handle this. But that would destroy real-time responsiveness. There is a need to automatically resolve conflicts so that every one ends up with same document state.&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;In Google docs, CRDTs&amp;nbsp; are used to handle concurrent text edits, ensuring that if users insert text at the same position, the system is able to resolve the order without conflicts.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjv_HHHjjLMJ_vM0LMA9WMd8za5u2p6QtRgdrzB0jkzp_w7CTQOjTV9xUkdc1rQdRhAF6Ue6X9hjs5ftzVDhwSZjyn2E0oawHWPeabjzOVaflqZfmx6aViWxm26f-7qLL6Ga3tVVsJt9Pet4I3cg8VkroJoQVpXKtG_HbkA3FZ57fetbT-WBoUKYeIrjjQ/s1248/image.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1248&quot; data-original-width=&quot;832&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjv_HHHjjLMJ_vM0LMA9WMd8za5u2p6QtRgdrzB0jkzp_w7CTQOjTV9xUkdc1rQdRhAF6Ue6X9hjs5ftzVDhwSZjyn2E0oawHWPeabjzOVaflqZfmx6aViWxm26f-7qLL6Ga3tVVsJt9Pet4I3cg8VkroJoQVpXKtG_HbkA3FZ57fetbT-WBoUKYeIrjjQ/w322-h320/image.jpg&quot; width=&quot;322&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;h3 data-end=&quot;193&quot; data-start=&quot;169&quot;&gt;What is a CRDT?&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-weight: normal;&quot;&gt;CRDT stands for conflict free replication data type.&lt;/span&gt;&lt;/h3&gt;&lt;p data-end=&quot;275&quot; data-start=&quot;194&quot;&gt;A &lt;strong data-end=&quot;204&quot; data-start=&quot;196&quot;&gt;CRDT&lt;/strong&gt; is a specially designed data structure for distributed systems that:&lt;/p&gt;&lt;p&gt;

&lt;/p&gt;&lt;ul data-end=&quot;598&quot; data-start=&quot;276&quot;&gt;
&lt;li data-end=&quot;335&quot; data-start=&quot;276&quot;&gt;
&lt;p data-end=&quot;335&quot; data-start=&quot;278&quot;&gt;Can be &lt;strong data-end=&quot;332&quot; data-start=&quot;285&quot;&gt;replicated across multiple nodes or regions&lt;/strong&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;445&quot; data-start=&quot;336&quot;&gt;
&lt;p data-end=&quot;445&quot; data-start=&quot;338&quot;&gt;Allows each replica to be updated independently and concurrently (without locks or central coordination).&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;598&quot; data-start=&quot;446&quot;&gt;
&lt;p data-end=&quot;598&quot; data-start=&quot;448&quot;&gt;Guarantees that all replicas will &lt;strong data-end=&quot;512&quot; data-start=&quot;482&quot;&gt;converge to the same state&lt;/strong&gt; eventually, &lt;strong data-end=&quot;546&quot; data-start=&quot;525&quot;&gt;without conflicts&lt;/strong&gt;, even if updates are applied in different orders.&lt;br /&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;h3 data-end=&quot;635&quot; data-start=&quot;605&quot;&gt;Why do we need CRDTs?&lt;/h3&gt;
&lt;p data-end=&quot;697&quot; data-start=&quot;636&quot;&gt;In collaborative editing (like Google Docs, Notion, Figma):&lt;/p&gt;
&lt;ul data-end=&quot;1042&quot; data-start=&quot;698&quot;&gt;
&lt;li data-end=&quot;753&quot; data-start=&quot;698&quot;&gt;
&lt;p data-end=&quot;753&quot; data-start=&quot;700&quot;&gt;Many users may edit the same document concurrently.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;853&quot; data-start=&quot;754&quot;&gt;
&lt;p data-end=&quot;853&quot; data-start=&quot;756&quot;&gt;Network latency or partitions mean updates may arrive in different orders at different servers.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;919&quot; data-start=&quot;854&quot;&gt;
&lt;p data-end=&quot;919&quot; data-start=&quot;856&quot;&gt;We can’t just “last-write-wins” — that would lose user edits.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1042&quot; data-start=&quot;920&quot;&gt;
&lt;p data-end=&quot;1042&quot; data-start=&quot;922&quot;&gt;We want &lt;strong data-end=&quot;957&quot; data-start=&quot;930&quot;&gt;low-latency local edits&lt;/strong&gt; (user sees their change immediately), with eventual consistency across the system.&lt;/p&gt;
&lt;/li&gt;&lt;li data-end=&quot;1042&quot; data-start=&quot;920&quot;&gt;&lt;p data-end=&quot;1042&quot; data-start=&quot;922&quot;&gt;Typical in distributed systems&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;1166&quot; data-start=&quot;1044&quot;&gt;CRDTs give us a way to allow users to edit &lt;strong data-end=&quot;1104&quot; data-start=&quot;1087&quot;&gt;locally first&lt;/strong&gt; and let the system reconcile changes without central locks.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;&lt;h3 data-end=&quot;1196&quot; data-start=&quot;1173&quot;&gt;Types of CRDTs&lt;/h3&gt;&lt;p data-end=&quot;1228&quot; data-start=&quot;1197&quot;&gt;There are two broad families:&lt;/p&gt;&lt;p data-end=&quot;1166&quot; data-start=&quot;1044&quot;&gt;

&lt;/p&gt;&lt;ol data-end=&quot;1665&quot; data-start=&quot;1230&quot;&gt;
&lt;li data-end=&quot;1419&quot; data-start=&quot;1230&quot;&gt;
&lt;p data-end=&quot;1277&quot; data-start=&quot;1233&quot;&gt;&lt;strong data-end=&quot;1275&quot; data-start=&quot;1233&quot;&gt;State-based (Convergent CRDTs, CvRDTs)&lt;/strong&gt;&lt;/p&gt;
&lt;ul data-end=&quot;1419&quot; data-start=&quot;1281&quot;&gt;
&lt;li data-end=&quot;1342&quot; data-start=&quot;1281&quot;&gt;
&lt;p data-end=&quot;1342&quot; data-start=&quot;1283&quot;&gt;Each replica occasionally sends its full state to others.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1419&quot; data-start=&quot;1346&quot;&gt;
&lt;p data-end=&quot;1419&quot; data-start=&quot;1348&quot;&gt;Merging = applying a mathematical &quot;join&quot; function (e.g., union, max).&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1665&quot; data-start=&quot;1421&quot;&gt;
&lt;p data-end=&quot;1473&quot; data-start=&quot;1424&quot;&gt;&lt;strong data-end=&quot;1471&quot; data-start=&quot;1424&quot;&gt;Operation-based (Commutative CRDTs, CmRDTs)&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;&lt;ul data-end=&quot;1665&quot; data-start=&quot;1477&quot;&gt;
&lt;li data-end=&quot;1563&quot; data-start=&quot;1477&quot;&gt;
&lt;p data-end=&quot;1563&quot; data-start=&quot;1479&quot;&gt;Each replica sends only the operations performed (e.g., &quot;insert X at position 2&quot;).&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1665&quot; data-start=&quot;1567&quot;&gt;
&lt;p data-end=&quot;1665&quot; data-start=&quot;1569&quot;&gt;These operations are designed so that applying them in any order yields the same final result.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/ol&gt;&lt;div&gt;&lt;h3 data-end=&quot;1710&quot; data-start=&quot;1672&quot;&gt;Examples of CRDTs in Practice&lt;/h3&gt;
&lt;ul data-end=&quot;2209&quot; data-start=&quot;1711&quot;&gt;
&lt;li data-end=&quot;1816&quot; data-start=&quot;1711&quot;&gt;
&lt;p data-end=&quot;1816&quot; data-start=&quot;1713&quot;&gt;&lt;strong data-end=&quot;1726&quot; data-start=&quot;1713&quot;&gt;G-Counter&lt;/strong&gt; (Grow-only counter): Each replica increments a local counter, merge = element-wise max.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1916&quot; data-start=&quot;1817&quot;&gt;
&lt;p data-end=&quot;1916&quot; data-start=&quot;1819&quot;&gt;&lt;strong data-end=&quot;1833&quot; data-start=&quot;1819&quot;&gt;PN-Counter&lt;/strong&gt; (Positive-Negative counter): Like G-counter, but supports increment &amp;amp; decrement.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1978&quot; data-start=&quot;1917&quot;&gt;
&lt;p data-end=&quot;1978&quot; data-start=&quot;1919&quot;&gt;&lt;strong data-end=&quot;1928&quot; data-start=&quot;1919&quot;&gt;G-Set&lt;/strong&gt; (Grow-only set): Only supports adding elements.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;2057&quot; data-start=&quot;1979&quot;&gt;
&lt;p data-end=&quot;2057&quot; data-start=&quot;1981&quot;&gt;&lt;strong data-end=&quot;1991&quot; data-start=&quot;1981&quot;&gt;OR-Set&lt;/strong&gt; (Observed-Remove set): Supports add &amp;amp; remove without ambiguity.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;2209&quot; data-start=&quot;2058&quot;&gt;
&lt;p data-end=&quot;2209&quot; data-start=&quot;2060&quot;&gt;&lt;strong data-end=&quot;2095&quot; data-start=&quot;2060&quot;&gt;RGA (Replicated Growable Array)&lt;/strong&gt; or &lt;strong data-end=&quot;2107&quot; data-start=&quot;2099&quot;&gt;WOOT&lt;/strong&gt; or &lt;strong data-end=&quot;2119&quot; data-start=&quot;2111&quot;&gt;LSEQ&lt;/strong&gt;: For collaborative text editing, where inserts/deletes happen at positions in a string.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;2331&quot; data-start=&quot;2211&quot;&gt;These are the basis for how real-time editors like Google Docs or Figma handle &lt;strong data-end=&quot;2328&quot; data-start=&quot;2293&quot;&gt;concurrent text/graphic editing&lt;/strong&gt;.&lt;br /&gt;&lt;br /&gt;Below is a simplistic Java implementation of a CRDT:&lt;br /&gt;&lt;br /&gt;&lt;a href=&quot;https://github.com/mdkhanga/blog-code/tree/master/general/src/main/java/com/mj/crdt&quot;&gt;https://github.com/mdkhanga/blog-code/tree/master/general/src/main/java/com/mj/crdt&lt;/a&gt;&lt;/p&gt;&lt;p data-end=&quot;2331&quot; data-start=&quot;2211&quot;&gt;The code above provides a simple implementation of a G-counter that supports insert, update, delete and merges replicas by taking the maximum value for each node. It is a starting point to understand how CRDTs ensure convergence in distributed systems.&lt;/p&gt;&lt;p data-end=&quot;2331&quot; data-start=&quot;2211&quot;&gt;&lt;b&gt;CRDT vs. Centralized Coordination&lt;/b&gt;&lt;/p&gt;&lt;p data-end=&quot;2331&quot; data-start=&quot;2211&quot;&gt;
&lt;/p&gt;&lt;ul data-end=&quot;2664&quot; data-start=&quot;2381&quot;&gt;
&lt;li data-end=&quot;2502&quot; data-start=&quot;2381&quot;&gt;
&lt;p data-end=&quot;2502&quot; data-start=&quot;2383&quot;&gt;If &lt;strong data-end=&quot;2416&quot; data-start=&quot;2386&quot;&gt;concurrent editing is rare&lt;/strong&gt; → a simple &lt;strong data-end=&quot;2462&quot; data-start=&quot;2428&quot;&gt;centralized lock/version check&lt;/strong&gt; may be enough (like your first idea).&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;2664&quot; data-start=&quot;2503&quot;&gt;
&lt;p data-end=&quot;2664&quot; data-start=&quot;2505&quot;&gt;If &lt;strong data-end=&quot;2540&quot; data-start=&quot;2508&quot;&gt;concurrent editing is common&lt;/strong&gt; (e.g., Figma boards with dozens of people) → you want &lt;strong data-end=&quot;2636&quot; data-start=&quot;2595&quot;&gt;CRDTs &lt;/strong&gt;&amp;nbsp;to avoid merge conflicts.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;strong data-end=&quot;2686&quot; data-start=&quot;2673&quot;&gt;In short:&lt;/strong&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;br data-end=&quot;2689&quot; data-start=&quot;2686&quot; /&gt;&lt;/b&gt;
A CRDT is a mathematically designed data structure that ensures all replicas in a distributed system converge to the same state without conflicts — perfect for &lt;strong data-end=&quot;2884&quot; data-start=&quot;2849&quot;&gt;real-time collaborative editing&lt;/strong&gt;.&lt;br /&gt;&lt;br /&gt;Note that this would be needed only for collaborative editing at scale in distributed systems. For anything else, it could be an overkill.&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/5702594110091639653'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/5702594110091639653'/><link rel='alternate' type='text/html' href='http://khangaonkar.blogspot.com/2025/09/crdt-tutorial-conflict-free-replication.html' title='CRDT Tutorial: Conflict Free Replication Data Types'/><author><name>Manoj Khangaonkar</name><uri>http://www.blogger.com/profile/13925926828045222969</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjv_HHHjjLMJ_vM0LMA9WMd8za5u2p6QtRgdrzB0jkzp_w7CTQOjTV9xUkdc1rQdRhAF6Ue6X9hjs5ftzVDhwSZjyn2E0oawHWPeabjzOVaflqZfmx6aViWxm26f-7qLL6Ga3tVVsJt9Pet4I3cg8VkroJoQVpXKtG_HbkA3FZ57fetbT-WBoUKYeIrjjQ/s72-w322-h320-c/image.jpg" height="72" width="72"/></entry><entry><id>tag:blogger.com,1999:blog-5008017311510568944.post-3066341380275040143</id><published>2025-08-30T17:22:00.000-07:00</published><updated>2025-08-31T16:28:17.533-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Archiecture"/><category scheme="http://www.blogger.com/atom/ns#" term="Cache"/><category scheme="http://www.blogger.com/atom/ns#" term="System Design"/><title type='text'>Cache in front of a slow database ?</title><content type='html'>&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;h1 data-end=&quot;271&quot; data-start=&quot;221&quot;&gt;Should You Front a Slow Database with a Cache?&lt;/h1&gt;
&lt;p data-end=&quot;431&quot; data-start=&quot;273&quot;&gt;Most of us have been there: a slow database query is dragging down response times, dashboards are red, and someone says, &lt;span data-end=&quot;429&quot; data-start=&quot;394&quot;&gt;&lt;i&gt;“Let’s put Redis in front of it.”&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;I have done it myself for an advertising system that needed response times of less than 30 ms. It worked very well.&lt;/span&gt;&lt;/p&gt;
&lt;p data-end=&quot;756&quot; data-start=&quot;433&quot;&gt;It’s a tried-and-true trick. Caching can take a query that costs &lt;strong data-end=&quot;526&quot; data-start=&quot;498&quot;&gt;hundreds of milliseconds&lt;/strong&gt; and make it return in &lt;strong data-end=&quot;578&quot; data-start=&quot;549&quot;&gt;single-digit milliseconds&lt;/strong&gt;. It reduces load on your database and makes your system feel “snappy.” But caching isn’t free — it introduces its own problems that engineers need to be very deliberate about.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi1yjfyxgYlqPXnyJNdAL1k7507SkR8lDJeK3F-a5GlAvCum11Vu03vG8nYZ7KIaWGHVvFKDqajqQXeFgHTiPqqQCBvUjbCOtoI0Nq7DH0Rn1RC6SLmXebP122NdoOK5TphBKbbZ-GrH7j2bs62sT94k6JnTkAc0RsFhHvBh4OUK3wKMM_AgSfUYAjKnVk/s1536/cache.png&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1024&quot; data-original-width=&quot;1536&quot; height=&quot;261&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi1yjfyxgYlqPXnyJNdAL1k7507SkR8lDJeK3F-a5GlAvCum11Vu03vG8nYZ7KIaWGHVvFKDqajqQXeFgHTiPqqQCBvUjbCOtoI0Nq7DH0Rn1RC6SLmXebP122NdoOK5TphBKbbZ-GrH7j2bs62sT94k6JnTkAc0RsFhHvBh4OUK3wKMM_AgSfUYAjKnVk/w370-h261/cache.png&quot; width=&quot;370&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;hr data-end=&quot;761&quot; data-start=&quot;758&quot; /&gt;
&lt;h2 data-end=&quot;794&quot; data-start=&quot;763&quot;&gt;Good Use Cases for Caching&lt;/h2&gt;
&lt;ul data-end=&quot;1244&quot; data-start=&quot;795&quot;&gt;
&lt;li data-end=&quot;954&quot; data-start=&quot;795&quot;&gt;
&lt;p data-end=&quot;954&quot; data-start=&quot;797&quot;&gt;&lt;strong data-end=&quot;821&quot; data-start=&quot;797&quot;&gt;Read-heavy workloads&lt;/strong&gt;&lt;br data-end=&quot;824&quot; data-start=&quot;821&quot; /&gt;
When the same data is read far more often than it’s written. For example, product catalogs, user profiles, or static metadata.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1106&quot; data-start=&quot;956&quot;&gt;
&lt;p data-end=&quot;1106&quot; data-start=&quot;958&quot;&gt;&lt;strong data-end=&quot;984&quot; data-start=&quot;958&quot;&gt;Expensive computations&lt;/strong&gt;&lt;br data-end=&quot;987&quot; data-start=&quot;984&quot; /&gt;
Search queries, aggregated analytics, or personalized recommendations where computing results on the fly is costly.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1244&quot; data-start=&quot;1108&quot;&gt;
&lt;p data-end=&quot;1244&quot; data-start=&quot;1110&quot;&gt;&lt;strong data-end=&quot;1127&quot; data-start=&quot;1110&quot;&gt;Burst traffic&lt;/strong&gt;&lt;br data-end=&quot;1130&quot; data-start=&quot;1127&quot; /&gt;
Handling sudden spikes (sales events, sports highlights, viral posts) where the database alone cannot keep up.&lt;/p&gt;
&lt;/li&gt;&lt;li data-end=&quot;1244&quot; data-start=&quot;1108&quot;&gt;&lt;p data-end=&quot;1244&quot; data-start=&quot;1110&quot;&gt;&lt;b&gt;Low latency requirements&lt;/b&gt;&lt;br /&gt;Some systems have low latency requirements. Clients need a response is say less than 50 ms or client aborts.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;1249&quot; data-start=&quot;1246&quot; /&gt;
&lt;h2 data-end=&quot;1285&quot; data-start=&quot;1251&quot;&gt;The Catch: Cache Consistency&lt;/h2&gt;
&lt;p data-end=&quot;1403&quot; data-start=&quot;1286&quot;&gt;The hardest part of caching isn’t adding Redis or Memcached — it’s &lt;strong data-end=&quot;1400&quot; data-start=&quot;1353&quot;&gt;keeping the cache in sync with the database&lt;/strong&gt;.&lt;/p&gt;
&lt;p data-end=&quot;1456&quot; data-start=&quot;1405&quot;&gt;Here are the main consistency issues you’ll face:&lt;/p&gt;
&lt;ol data-end=&quot;2019&quot; data-start=&quot;1458&quot;&gt;
&lt;li data-end=&quot;1688&quot; data-start=&quot;1458&quot;&gt;
&lt;p data-end=&quot;1688&quot; data-start=&quot;1461&quot;&gt;&lt;strong data-end=&quot;1475&quot; data-start=&quot;1461&quot;&gt;Stale Data&lt;/strong&gt;&lt;br data-end=&quot;1478&quot; data-start=&quot;1475&quot; /&gt;
If the cache isn’t updated when the database changes, users may see outdated results.&lt;br data-end=&quot;1569&quot; data-start=&quot;1566&quot; /&gt;
Example: A user updates their shipping address, but the checkout flow still shows the old one because it’s cached.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;1852&quot; data-start=&quot;1690&quot;&gt;
&lt;p data-end=&quot;1852&quot; data-start=&quot;1693&quot;&gt;&lt;strong data-end=&quot;1715&quot; data-start=&quot;1693&quot;&gt;Cache Invalidation&lt;/strong&gt;&lt;br data-end=&quot;1718&quot; data-start=&quot;1715&quot; /&gt;
The classic hard problem: &lt;em data-end=&quot;1782&quot; data-start=&quot;1747&quot;&gt;When do you expire cache entries?&lt;/em&gt; Too soon → database load spikes. Too late → users see stale values.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;2019&quot; data-start=&quot;1854&quot;&gt;
&lt;p data-end=&quot;2019&quot; data-start=&quot;1857&quot;&gt;&lt;strong data-end=&quot;1876&quot; data-start=&quot;1857&quot;&gt;Race Conditions&lt;/strong&gt;&lt;br data-end=&quot;1879&quot; data-start=&quot;1876&quot; /&gt;
Writes may hit the database while another process is still serving old cache data. Without careful ordering, you risk “losing” updates.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-end=&quot;2024&quot; data-start=&quot;2021&quot; /&gt;
&lt;h2 data-end=&quot;2050&quot; data-start=&quot;2026&quot;&gt;Common Strategies&lt;/h2&gt;
&lt;ul data-end=&quot;2718&quot; data-start=&quot;2051&quot;&gt;
&lt;li data-end=&quot;2243&quot; data-start=&quot;2051&quot;&gt;
&lt;p data-end=&quot;2243&quot; data-start=&quot;2053&quot;&gt;&lt;strong data-end=&quot;2083&quot; data-start=&quot;2053&quot;&gt;Cache Aside (Lazy Loading)&lt;/strong&gt;&lt;br data-end=&quot;2086&quot; data-start=&quot;2083&quot; /&gt;
Application checks cache → if miss, fetch from DB → populate cache.&lt;br data-end=&quot;2158&quot; data-start=&quot;2155&quot; /&gt;
✅ Simple, common.&lt;br data-end=&quot;2180&quot; data-start=&quot;2177&quot; /&gt;
❌ Risk of stale data unless you also invalidate on updates.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;2399&quot; data-start=&quot;2245&quot;&gt;
&lt;p data-end=&quot;2399&quot; data-start=&quot;2247&quot;&gt;&lt;strong data-end=&quot;2264&quot; data-start=&quot;2247&quot;&gt;Write-Through&lt;/strong&gt;&lt;br data-end=&quot;2267&quot; data-start=&quot;2264&quot; /&gt;
Writes always go through the cache → cache updates DB.&lt;br data-end=&quot;2326&quot; data-start=&quot;2323&quot; /&gt;
✅ Consistency is better.&lt;br data-end=&quot;2355&quot; data-start=&quot;2352&quot; /&gt;
❌ Higher write latency, more complexity.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;2568&quot; data-start=&quot;2401&quot;&gt;
&lt;p data-end=&quot;2568&quot; data-start=&quot;2403&quot;&gt;&lt;strong data-end=&quot;2419&quot; data-start=&quot;2403&quot;&gt;Write-Behind&lt;/strong&gt;&lt;br data-end=&quot;2422&quot; data-start=&quot;2419&quot; /&gt;
Writes update the cache, and DB updates happen asynchronously.&lt;br data-end=&quot;2489&quot; data-start=&quot;2486&quot; /&gt;
✅ Fast writes.&lt;br data-end=&quot;2508&quot; data-start=&quot;2505&quot; /&gt;
❌ Risk of data loss if cache fails before DB is updated.&lt;/p&gt;
&lt;/li&gt;
&lt;li data-end=&quot;2718&quot; data-start=&quot;2570&quot;&gt;
&lt;p data-end=&quot;2718&quot; data-start=&quot;2572&quot;&gt;&lt;strong data-end=&quot;2594&quot; data-start=&quot;2572&quot;&gt;Time-to-Live (TTL)&lt;/strong&gt;&lt;br data-end=&quot;2597&quot; data-start=&quot;2594&quot; /&gt;
Expire cache entries after a set period.&lt;br data-end=&quot;2642&quot; data-start=&quot;2639&quot; /&gt;
✅ Easy safety net.&lt;br data-end=&quot;2665&quot; data-start=&quot;2662&quot; /&gt;
❌ Not precise; stale reads possible until expiry.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-end=&quot;2723&quot; data-start=&quot;2720&quot; /&gt;
&lt;h2 data-end=&quot;2750&quot; data-start=&quot;2725&quot;&gt;So, Is It Worth It?&lt;/h2&gt;
&lt;p data-end=&quot;2886&quot; data-start=&quot;2751&quot;&gt;If your workload is &lt;strong data-end=&quot;2853&quot; data-start=&quot;2771&quot;&gt;read-heavy, latency-sensitive, and relatively tolerant of eventual consistency&lt;/strong&gt;, caching is usually a big win.&lt;/p&gt;
&lt;p data-end=&quot;3058&quot; data-start=&quot;2888&quot;&gt;But if your workload is &lt;strong data-end=&quot;2958&quot; data-start=&quot;2912&quot;&gt;write-heavy or requires strict consistency&lt;/strong&gt; (think payments, inventory, or medical records), caching can create more problems than it solves.&lt;/p&gt;
&lt;p data-end=&quot;3268&quot; data-start=&quot;3060&quot;&gt;The lesson: don’t add Redis or Memcached just because they’re shiny tools. Add them because you’ve carefully measured your system, know where the bottleneck is, and can live with the consistency trade-offs.&lt;/p&gt;
&lt;hr data-end=&quot;3273&quot; data-start=&quot;3270&quot; /&gt;
&lt;p data-end=&quot;3439&quot; data-start=&quot;3275&quot;&gt;&lt;strong data-end=&quot;3290&quot; data-start=&quot;3278&quot;&gt;Takeaway&lt;/strong&gt;:&lt;br data-end=&quot;3294&quot; data-start=&quot;3291&quot; /&gt;
Caching is like nitrous oxide for your system — it can make things blazing fast, but you need to handle it with care or you’ll blow the engine.&lt;/p&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/3066341380275040143'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/3066341380275040143'/><link rel='alternate' type='text/html' href='http://khangaonkar.blogspot.com/2025/08/caching-with-redis-for-slow-database.html' title='Cache in front of a slow database ?'/><author><name>Manoj Khangaonkar</name><uri>http://www.blogger.com/profile/13925926828045222969</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi1yjfyxgYlqPXnyJNdAL1k7507SkR8lDJeK3F-a5GlAvCum11Vu03vG8nYZ7KIaWGHVvFKDqajqQXeFgHTiPqqQCBvUjbCOtoI0Nq7DH0Rn1RC6SLmXebP122NdoOK5TphBKbbZ-GrH7j2bs62sT94k6JnTkAc0RsFhHvBh4OUK3wKMM_AgSfUYAjKnVk/s72-w370-h261-c/cache.png" height="72" width="72"/></entry><entry><id>tag:blogger.com,1999:blog-5008017311510568944.post-8996040184764449825</id><published>2025-08-28T04:45:00.000-07:00</published><updated>2025-08-28T12:03:36.165-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="AI"/><category scheme="http://www.blogger.com/atom/ns#" term="AI Agent"/><category scheme="http://www.blogger.com/atom/ns#" term="Code generation"/><category scheme="http://www.blogger.com/atom/ns#" term="Large language Model"/><category scheme="http://www.blogger.com/atom/ns#" term="LLM"/><title type='text'> The Unsung Heroes Behind Your AI Coding Assistant </title><content type='html'>&lt;div style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;background-color: white; color: rgba(0, 0, 0, 0.9); font-family: -apple-system, system-ui, &amp;quot;system-ui&amp;quot;, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, &amp;quot;Fira Sans&amp;quot;, Ubuntu, Oxygen, &amp;quot;Oxygen Sans&amp;quot;, Cantarell, &amp;quot;Droid Sans&amp;quot;, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Lucida Grande&amp;quot;, Helvetica, Arial, sans-serif; font-size: 20px; white-space-collapse: preserve;&quot;&gt;While everyone&#39;s talking about ChatGPT and tools like Cursor, Windsurf, and GitHub Copilot transforming how we code, let&#39;s shine a light on the specialized models that actually power these coding experiences.&lt;/span&gt;&lt;/div&gt;&lt;p style=&quot;background: none 0% 0% / auto repeat scroll padding-box border-box rgb(255, 255, 255); border-color: rgba(0, 0, 0, 0.9); border-image: none 100% / 1 / 0 stretch; border-style: none; border-width: 0px; box-sizing: inherit; color: rgba(0, 0, 0, 0.9); counter-reset: list-1 0 list-2 0 list-3 0 list-4 0 list-5 0 list-6 0 list-7 0 list-8 0 list-9 0; cursor: text; font-family: -apple-system, system-ui, &amp;quot;system-ui&amp;quot;, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, &amp;quot;Fira Sans&amp;quot;, Ubuntu, Oxygen, &amp;quot;Oxygen Sans&amp;quot;, Cantarell, &amp;quot;Droid Sans&amp;quot;, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Lucida Grande&amp;quot;, Helvetica, Arial, sans-serif; font-size: 20px; line-height: 1.5; margin: 0px; padding: 0px; vertical-align: baseline; white-space-collapse: preserve;&quot;&gt;&lt;br style=&quot;box-sizing: inherit;&quot; /&gt;&lt;/p&gt;&lt;p style=&quot;background: none 0% 0% / auto repeat scroll padding-box border-box rgb(255, 255, 255); border-color: rgba(0, 0, 0, 0.9); border-image: none 100% / 1 / 0 stretch; border-style: none; border-width: 0px; box-sizing: inherit; color: rgba(0, 0, 0, 0.9); counter-reset: list-1 0 list-2 0 list-3 0 list-4 0 list-5 0 list-6 0 list-7 0 list-8 0 list-9 0; cursor: text; font-family: -apple-system, system-ui, &amp;quot;system-ui&amp;quot;, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, &amp;quot;Fira Sans&amp;quot;, Ubuntu, Oxygen, &amp;quot;Oxygen Sans&amp;quot;, Cantarell, &amp;quot;Droid Sans&amp;quot;, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Lucida Grande&amp;quot;, Helvetica, Arial, sans-serif; font-size: 20px; line-height: 1.5; margin: 0px; padding: 0px; vertical-align: baseline; white-space-collapse: preserve;&quot;&gt;Meet the Code Generation Champions:&lt;/p&gt;&lt;p style=&quot;background: none 0% 0% / auto repeat scroll padding-box border-box rgb(255, 255, 255); border-color: rgba(0, 0, 0, 0.9); border-image: none 100% / 1 / 0 stretch; border-style: none; border-width: 0px; box-sizing: inherit; color: rgba(0, 0, 0, 0.9); counter-reset: list-1 0 list-2 0 list-3 0 list-4 0 list-5 0 list-6 0 list-7 0 list-8 0 list-9 0; cursor: text; font-family: -apple-system, system-ui, &amp;quot;system-ui&amp;quot;, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, &amp;quot;Fira Sans&amp;quot;, Ubuntu, Oxygen, &amp;quot;Oxygen Sans&amp;quot;, Cantarell, &amp;quot;Droid Sans&amp;quot;, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Lucida Grande&amp;quot;, Helvetica, Arial, sans-serif; font-size: 20px; line-height: 1.5; margin: 0px; padding: 0px; vertical-align: baseline; white-space-collapse: preserve;&quot;&gt;&lt;br style=&quot;box-sizing: inherit;&quot; /&gt;&lt;/p&gt;&lt;p style=&quot;background: none 0% 0% / auto repeat scroll padding-box border-box rgb(255, 255, 255); border-color: rgba(0, 0, 0, 0.9); border-image: none 100% / 1 / 0 stretch; border-style: none; border-width: 0px; box-sizing: inherit; color: rgba(0, 0, 0, 0.9); counter-reset: list-1 0 list-2 0 list-3 0 list-4 0 list-5 0 list-6 0 list-7 0 list-8 0 list-9 0; cursor: text; font-family: -apple-system, system-ui, &amp;quot;system-ui&amp;quot;, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, &amp;quot;Fira Sans&amp;quot;, Ubuntu, Oxygen, &amp;quot;Oxygen Sans&amp;quot;, Cantarell, &amp;quot;Droid Sans&amp;quot;, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Lucida Grande&amp;quot;, Helvetica, Arial, sans-serif; font-size: 20px; line-height: 1.5; margin: 0px; padding: 0px; vertical-align: baseline; white-space-collapse: preserve;&quot;&gt;&lt;/p&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;StarCoder - Trained on 80+ programming languages from GitHub repos, this open-source model excels at code completion and generation&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;/p&gt;&lt;p style=&quot;background: none 0% 0% / auto repeat scroll padding-box border-box rgb(255, 255, 255); border-color: rgba(0, 0, 0, 0.9); border-image: none 100% / 1 / 0 stretch; border-style: none; border-width: 0px; box-sizing: inherit; color: rgba(0, 0, 0, 0.9); counter-reset: list-1 0 list-2 0 list-3 0 list-4 0 list-5 0 list-6 0 list-7 0 list-8 0 list-9 0; cursor: text; font-family: -apple-system, system-ui, &amp;quot;system-ui&amp;quot;, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, &amp;quot;Fira Sans&amp;quot;, Ubuntu, Oxygen, &amp;quot;Oxygen Sans&amp;quot;, Cantarell, &amp;quot;Droid Sans&amp;quot;, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Lucida Grande&amp;quot;, Helvetica, Arial, sans-serif; font-size: 20px; line-height: 1.5; margin: 0px; padding: 0px; vertical-align: baseline; white-space-collapse: preserve;&quot;&gt;&lt;/p&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;CodeT5 - Google&#39;s encoder-decoder model that understands code structure and can translate between languages&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;/p&gt;&lt;p style=&quot;background: none 0% 0% / auto repeat scroll padding-box border-box rgb(255, 255, 255); border-color: rgba(0, 0, 0, 0.9); border-image: none 100% / 1 / 0 stretch; border-style: none; border-width: 0px; box-sizing: inherit; color: rgba(0, 0, 0, 0.9); counter-reset: list-1 0 list-2 0 list-3 0 list-4 0 list-5 0 list-6 0 list-7 0 list-8 0 list-9 0; cursor: text; font-family: -apple-system, system-ui, &amp;quot;system-ui&amp;quot;, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, &amp;quot;Fira Sans&amp;quot;, Ubuntu, Oxygen, &amp;quot;Oxygen Sans&amp;quot;, Cantarell, &amp;quot;Droid Sans&amp;quot;, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Lucida Grande&amp;quot;, Helvetica, Arial, sans-serif; font-size: 20px; line-height: 1.5; margin: 0px; padding: 0px; vertical-align: baseline; white-space-collapse: preserve;&quot;&gt;&lt;/p&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;InCoder - Meta&#39;s bidirectional model that can fill in code gaps, not just complete from left to right&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;/p&gt;&lt;p style=&quot;background: none 0% 0% / auto repeat scroll padding-box border-box rgb(255, 255, 255); border-color: rgba(0, 0, 0, 0.9); border-image: none 100% / 1 / 0 stretch; border-style: none; border-width: 0px; box-sizing: inherit; color: rgba(0, 0, 0, 0.9); counter-reset: list-1 0 list-2 0 list-3 0 list-4 0 list-5 0 list-6 0 list-7 0 list-8 0 list-9 0; cursor: text; font-family: -apple-system, system-ui, &amp;quot;system-ui&amp;quot;, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, &amp;quot;Fira Sans&amp;quot;, Ubuntu, Oxygen, &amp;quot;Oxygen Sans&amp;quot;, Cantarell, &amp;quot;Droid Sans&amp;quot;, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Lucida Grande&amp;quot;, Helvetica, Arial, sans-serif; font-size: 20px; line-height: 1.5; margin: 0px; padding: 0px; vertical-align: baseline; white-space-collapse: preserve;&quot;&gt;&lt;/p&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;CodeGen - Salesforce&#39;s autoregressive model trained on both natural language and code&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;/p&gt;&lt;p style=&quot;background: none 0% 0% / auto repeat scroll padding-box border-box rgb(255, 255, 255); border-color: rgba(0, 0, 0, 0.9); border-image: none 100% / 1 / 0 stretch; border-style: none; border-width: 0px; box-sizing: inherit; color: rgba(0, 0, 0, 0.9); counter-reset: list-1 0 list-2 0 list-3 0 list-4 0 list-5 0 list-6 0 list-7 0 list-8 0 list-9 0; cursor: text; font-family: -apple-system, system-ui, &amp;quot;system-ui&amp;quot;, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, &amp;quot;Fira Sans&amp;quot;, Ubuntu, Oxygen, &amp;quot;Oxygen Sans&amp;quot;, Cantarell, &amp;quot;Droid Sans&amp;quot;, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Lucida Grande&amp;quot;, Helvetica, Arial, sans-serif; font-size: 20px; line-height: 1.5; margin: 0px; padding: 0px; vertical-align: baseline; white-space-collapse: preserve;&quot;&gt;&lt;/p&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;Codex (OpenAI) - The foundation behind GitHub Copilot, though now evolved into GPT-4 variants&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;/p&gt;&lt;p style=&quot;background: none 0% 0% / auto repeat scroll padding-box border-box rgb(255, 255, 255); border-color: rgba(0, 0, 0, 0.9); border-image: none 100% / 1 / 0 stretch; border-style: none; border-width: 0px; box-sizing: inherit; color: rgba(0, 0, 0, 0.9); counter-reset: list-1 0 list-2 0 list-3 0 list-4 0 list-5 0 list-6 0 list-7 0 list-8 0 list-9 0; cursor: text; font-family: -apple-system, system-ui, &amp;quot;system-ui&amp;quot;, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, &amp;quot;Fira Sans&amp;quot;, Ubuntu, Oxygen, &amp;quot;Oxygen Sans&amp;quot;, Cantarell, &amp;quot;Droid Sans&amp;quot;, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Lucida Grande&amp;quot;, Helvetica, Arial, sans-serif; font-size: 20px; line-height: 1.5; margin: 0px; padding: 0px; vertical-align: baseline; white-space-collapse: preserve;&quot;&gt;&lt;br style=&quot;box-sizing: inherit;&quot; /&gt;&lt;/p&gt;&lt;p style=&quot;background: none 0% 0% / auto repeat scroll padding-box border-box rgb(255, 255, 255); border-color: rgba(0, 0, 0, 0.9); border-image: none 100% / 1 / 0 stretch; border-style: none; border-width: 0px; box-sizing: inherit; color: rgba(0, 0, 0, 0.9); counter-reset: list-1 0 list-2 0 list-3 0 list-4 0 list-5 0 list-6 0 list-7 0 list-8 0 list-9 0; cursor: text; font-family: -apple-system, system-ui, &amp;quot;system-ui&amp;quot;, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, &amp;quot;Fira Sans&amp;quot;, Ubuntu, Oxygen, &amp;quot;Oxygen Sans&amp;quot;, Cantarell, &amp;quot;Droid Sans&amp;quot;, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Lucida Grande&amp;quot;, Helvetica, Arial, sans-serif; font-size: 20px; line-height: 1.5; margin: 0px; padding: 0px; vertical-align: baseline; white-space-collapse: preserve;&quot;&gt;What makes these different from general LLMs?&lt;/p&gt;&lt;p style=&quot;background: none 0% 0% / auto repeat scroll padding-box border-box rgb(255, 255, 255); border-color: rgba(0, 0, 0, 0.9); border-image: none 100% / 1 / 0 stretch; border-style: none; border-width: 0px; box-sizing: inherit; color: rgba(0, 0, 0, 0.9); counter-reset: list-1 0 list-2 0 list-3 0 list-4 0 list-5 0 list-6 0 list-7 0 list-8 0 list-9 0; cursor: text; font-family: -apple-system, system-ui, &amp;quot;system-ui&amp;quot;, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, &amp;quot;Fira Sans&amp;quot;, Ubuntu, Oxygen, &amp;quot;Oxygen Sans&amp;quot;, Cantarell, &amp;quot;Droid Sans&amp;quot;, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Lucida Grande&amp;quot;, Helvetica, Arial, sans-serif; font-size: 20px; line-height: 1.5; margin: 0px; padding: 0px; vertical-align: baseline; white-space-collapse: preserve;&quot;&gt;&lt;/p&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;Trained on massive code repositories (billions of lines) &lt;/li&gt;&lt;li&gt;Understand syntax, semantics, and programming patterns &lt;/li&gt;&lt;li&gt;Can maintain context across entire codebases &lt;/li&gt;&lt;li&gt;Specialized in code-specific tasks like debugging, refactoring, and documentation&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;/p&gt;&lt;p style=&quot;background: none 0% 0% / auto repeat scroll padding-box border-box rgb(255, 255, 255); border-color: rgba(0, 0, 0, 0.9); border-image: none 100% / 1 / 0 stretch; border-style: none; border-width: 0px; box-sizing: inherit; color: rgba(0, 0, 0, 0.9); counter-reset: list-1 0 list-2 0 list-3 0 list-4 0 list-5 0 list-6 0 list-7 0 list-8 0 list-9 0; cursor: text; font-family: -apple-system, system-ui, &amp;quot;system-ui&amp;quot;, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, &amp;quot;Fira Sans&amp;quot;, Ubuntu, Oxygen, &amp;quot;Oxygen Sans&amp;quot;, Cantarell, &amp;quot;Droid Sans&amp;quot;, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Lucida Grande&amp;quot;, Helvetica, Arial, sans-serif; font-size: 20px; line-height: 1.5; margin: 0px; padding: 0px; vertical-align: baseline; white-space-collapse: preserve;&quot;&gt;&lt;br style=&quot;box-sizing: inherit;&quot; /&gt;&lt;/p&gt;&lt;div style=&quot;background: none 0% 0% / auto repeat scroll padding-box border-box rgb(255, 255, 255); border-color: rgba(0, 0, 0, 0.9); border-image: none 100% / 1 / 0 stretch; border-style: none; border-width: 0px; box-sizing: inherit; color: rgba(0, 0, 0, 0.9); counter-reset: list-1 0 list-2 0 list-3 0 list-4 0 list-5 0 list-6 0 list-7 0 list-8 0 list-9 0; cursor: text; font-family: -apple-system, system-ui, &amp;quot;system-ui&amp;quot;, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, &amp;quot;Fira Sans&amp;quot;, Ubuntu, Oxygen, &amp;quot;Oxygen Sans&amp;quot;, Cantarell, &amp;quot;Droid Sans&amp;quot;, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Lucida Grande&amp;quot;, Helvetica, Arial, sans-serif; font-size: 20px; line-height: 1.5; margin: 0px; padding: 0px; text-align: left; vertical-align: baseline; white-space-collapse: preserve;&quot;&gt;The magic isn&#39;t just in having &quot;AI that codes&quot; - it&#39;s in having models that truly understand the intricacies of software development. They aren’t just regurgitating text—they’re tuned for the nuances of programming, which makes them invaluable for developers. These specialized architectures are why your AI assistant can suggest that perfect function name or catch that subtle bug you&#39;ve been hunting for hours. &lt;/div&gt;&lt;p style=&quot;background: none 0% 0% / auto repeat scroll padding-box border-box rgb(255, 255, 255); border-color: rgba(0, 0, 0, 0.9); border-image: none 100% / 1 / 0 stretch; border-style: none; border-width: 0px; box-sizing: inherit; color: rgba(0, 0, 0, 0.9); counter-reset: list-1 0 list-2 0 list-3 0 list-4 0 list-5 0 list-6 0 list-7 0 list-8 0 list-9 0; cursor: text; font-family: -apple-system, system-ui, &amp;quot;system-ui&amp;quot;, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, &amp;quot;Fira Sans&amp;quot;, Ubuntu, Oxygen, &amp;quot;Oxygen Sans&amp;quot;, Cantarell, &amp;quot;Droid Sans&amp;quot;, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Lucida Grande&amp;quot;, Helvetica, Arial, sans-serif; font-size: 20px; line-height: 1.5; margin: 0px; padding: 0px; vertical-align: baseline; white-space-collapse: preserve;&quot;&gt;&lt;br style=&quot;box-sizing: inherit;&quot; /&gt;&lt;/p&gt;&lt;p style=&quot;background: none 0% 0% / auto repeat scroll padding-box border-box rgb(255, 255, 255); border-color: rgba(0, 0, 0, 0.9); border-image: none 100% / 1 / 0 stretch; border-style: none; border-width: 0px; box-sizing: inherit; color: rgba(0, 0, 0, 0.9); counter-reset: list-1 0 list-2 0 list-3 0 list-4 0 list-5 0 list-6 0 list-7 0 list-8 0 list-9 0; cursor: text; font-family: -apple-system, system-ui, &amp;quot;system-ui&amp;quot;, &amp;quot;Segoe UI&amp;quot;, Roboto, &amp;quot;Helvetica Neue&amp;quot;, &amp;quot;Fira Sans&amp;quot;, Ubuntu, Oxygen, &amp;quot;Oxygen Sans&amp;quot;, Cantarell, &amp;quot;Droid Sans&amp;quot;, &amp;quot;Apple Color Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Emoji&amp;quot;, &amp;quot;Segoe UI Symbol&amp;quot;, &amp;quot;Lucida Grande&amp;quot;, Helvetica, Arial, sans-serif; font-size: 20px; line-height: 1.5; margin: 0px; padding: 0px; vertical-align: baseline; white-space-collapse: preserve;&quot;&gt;The real game-changer? Most of these models are open-source, democratizing access to powerful coding assistance beyond just the big tech companies.
&lt;br /&gt;&lt;/p&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/8996040184764449825'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/8996040184764449825'/><link rel='alternate' type='text/html' href='http://khangaonkar.blogspot.com/2025/08/the-unsung-heroes-behind-your-ai-coding.html' title=' The Unsung Heroes Behind Your AI Coding Assistant '/><author><name>Manoj Khangaonkar</name><uri>http://www.blogger.com/profile/13925926828045222969</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-5008017311510568944.post-2690064827566322775</id><published>2025-08-24T14:38:00.000-07:00</published><updated>2025-08-24T14:38:50.028-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="JAVA concurrency scalability"/><title type='text'>JDK 21 Virtual threads: The end of regular threads ? Not quite.</title><content type='html'>&lt;p&gt;&amp;nbsp;A question I get asked all the time: If JDK 21 supports virtual threads, do I ever need to use regular threads ?&lt;/p&gt;&lt;p&gt;Java 21 brought us virtual threads, a game-changer for writing highly concurrent applications. Their lightweight nature and massive scalability are incredibly appealing. It&#39;s natural to wonder: do we even need regular platform (OS) threads anymore?&lt;/p&gt;&lt;p&gt;While virtual threads are fantastic for many I/O-bound workloads, there are still scenarios where platform threads remain relevant. Here&#39;s why:&lt;/p&gt;&lt;p&gt;&lt;b&gt;1. CPU-Bound Tasks:&lt;/b&gt;&lt;/p&gt;&lt;p&gt;Virtual threads yield the carrier thread when they perform blocking I/O operations. However, for purely CPU-bound tasks, they don&#39;t offer a significant advantage over platform threads in terms of raw processing power. In fact, the context switching involved might introduce a tiny bit of overhead.&lt;/p&gt;&lt;p&gt;Consider a computationally intensive task like calculating factorials:&lt;/p&gt;&lt;p&gt;Virtual threads example:&lt;br /&gt;
&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;
// A CPU-intensive task
Runnable cpuBoundTask = () -&amp;gt; {
    long result = 1;
    for (int i = 1; i &amp;lt;= 10000; i++) {
        result *= i;
    }
    System.out.println(&quot;Virtual thread task finished.&quot;);
};

// Start a virtual thread for the task
Thread.startVirtualThread(cpuBoundTask);
&lt;/code&gt;&lt;/pre&gt; &lt;br /&gt;&lt;br /&gt;Platform threads example:&lt;br /&gt;
&lt;pre&gt;&lt;code&gt;
Runnable cpuBoundTask = () -&amp;gt; {
    long result = 1;
    for (int i = 1; i &amp;lt;= 10000; i++) {
        result *= i;
    }
    System.out.println(&quot;Platform thread task finished.&quot;);
};

// Start a regular platform thread
new Thread(cpuBoundTask).start();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For sustained CPU-bound work, managing a smaller pool of platform threads might still be a more efficient approach to leverage the underlying hardware.&lt;/p&gt;&lt;p&gt;&lt;b&gt;2. Integration with Native Code and External Libraries:&lt;/b&gt;&lt;/p&gt;&lt;p&gt;Some native libraries or older Java APIs might have specific requirements or behaviors when used with threads. Virtual threads, being a newer abstraction, might not be fully compatible or optimally performant with all such integrations. Platform threads, being closer to the operating system&#39;s threading model, often provide better compatibility in these scenarios.&lt;/p&gt;&lt;p&gt;&lt;b&gt;3. Thread-Local Variables with Care:&lt;/b&gt;&lt;/p&gt;&lt;p&gt;While virtual threads support thread-local variables, their potentially large number can lead to increased memory consumption if thread-locals are heavily used and store significant data. With platform threads, you typically have a smaller, more controlled number of threads, making it easier to reason about thread-local usage. However, it&#39;s crucial to manage thread-locals carefully in both models to avoid memory leaks.&lt;/p&gt;&lt;p&gt;&lt;b&gt;4. Profiling and Debugging:&lt;/b&gt;&lt;/p&gt;&lt;p&gt;The tooling around thread analysis and debugging is more mature for platform threads. While support for virtual threads is rapidly improving, there might be cases where existing profiling tools offer more in-depth insights for platform threads.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;5. Backward compatibility&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;If you want you library or server to be available to users who are on JDKs earlier than JDK21, then you have no choice but to use regular threads. Virtual threads are not just a new library; they are a fundamental change to the Java Virtual Machine&#39;s threading model (part of Project Loom). The underlying code that manages and schedules virtual threads on top of carrier threads is not present in older JVMs. This can be one of the most important reasons for using platform threads.&lt;/p&gt;&lt;p&gt;&lt;b&gt;In Conclusion:&lt;/b&gt;&lt;/p&gt;&lt;p&gt;Virtual threads are a powerful addition to the Java concurrency landscape and will undoubtedly become the default choice for many concurrent applications, especially those with high I/O. However, platform threads still have their place, particularly for CPU-bound tasks, legacy integrations, and situations requiring fine-grained control over thread management.&lt;/p&gt;&lt;p&gt;Understanding the nuances of both models will allow you to make informed decisions and build more efficient and robust Java applications.&lt;/p&gt;&lt;p&gt;&lt;/p&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/2690064827566322775'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/2690064827566322775'/><link rel='alternate' type='text/html' href='http://khangaonkar.blogspot.com/2025/08/jdk-21-virtual-threads-end-of-regular.html' title='JDK 21 Virtual threads: The end of regular threads ? Not quite.'/><author><name>Manoj Khangaonkar</name><uri>http://www.blogger.com/profile/13925926828045222969</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-5008017311510568944.post-4720722268172114351</id><published>2025-05-04T17:48:00.000-07:00</published><updated>2025-05-08T17:55:16.400-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Databases"/><category scheme="http://www.blogger.com/atom/ns#" term="Distributed systems"/><title type='text'> Understanding Isolation levels vs Consistency levels</title><content type='html'>&lt;p&gt;In databases, the terms isolation level and consistency level/model are sometimes used interchangeably. &quot;Read repeatable&quot; and &quot;Serializable&quot; are well known isolation levels. But &quot;Strict Serializable&quot; and &quot;Linearizable&quot; are consistency terms.&amp;nbsp;&lt;/p&gt;&lt;p&gt;If you have used Mysql or Postgresql, you know probably know what an isolation levels like &quot;Read repeatable&quot; or &quot;Serializable&quot; means. But when you work on a distributed database you hear about consistency level much more.&lt;/p&gt;&lt;p&gt;The first time I heard about consistency level was when I worked with Apache Cassandra which claimed to only support &quot;eventual consistency&quot;.&amp;nbsp; A few years ago when my company was evaluating distributed databases, we had a few architects that insisted that we needed a database that support &quot;strict serializability&quot;. CockroachDB was a database that supported this consistency level.&lt;/p&gt;&lt;p&gt;If you are confused, read long. I wrote this blog in attempt to clear up my confusion.&lt;/p&gt;&lt;p&gt;So far, the best explanations on this topic that I found are by Daniel J Abadi [2] [3]. Kyle Kingsbury @Jepson [1] has good descriptions of the topic as well.&lt;/p&gt;&lt;p&gt;But first, a clarification on what consistency means.&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;What is consistency ?&lt;/h2&gt;&lt;p&gt;Consistency is an overloaded term and its meaning has changed in recent times.&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;ACID consistency&lt;/h3&gt;&lt;p&gt;The database must preserve its internal correctness rules after every transaction.&lt;/p&gt;&lt;p&gt;Consider a banking database with a constraint account_balance &amp;gt; 0.&amp;nbsp;&lt;/p&gt;&lt;p&gt;If the starting account_balance is 50 and a transaction tried to deduct 100, that is a violation of that constraint and should fail.&lt;/p&gt;&lt;p&gt;This is the C is ACID. Databases support constraints to ensure this. But it is mainly the responsibility of the application programmer. It is well understood and rarely discussed these days.&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Distributed systems consistency&lt;/h3&gt;&lt;p&gt;The system must ensure that all nodes (or clients) agree on the same view of data.&lt;/p&gt;&lt;p&gt;Make the distributed system feel like a single threaded single node system. Read of a value any where in the system produces the same result [2]. The result returned is the most recently written value no matter where it was written.&lt;/p&gt;&lt;p&gt;Consider a system with multiple nodes. X was 1. The value X=2 is written to one node and replicated to others. If clients read from the replicas. Do they all see X=2 immediately ? With strict serializable consistency level , the answer is yes. With weaker models, it is possible they read a an older value.&amp;nbsp;&lt;/p&gt;&lt;p&gt;Most of us first heard of this description from the CAP theorem.&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Why the difference ?&lt;/h3&gt;&lt;p&gt;Both describe behavior under concurrency.&amp;nbsp;&lt;/p&gt;&lt;p&gt;Isolation levels describe problems that occurs in single node databases when transactions execute concurrently. At the highest isolation level transactions execute in some order. Each transaction executes as if it were alone.&lt;/p&gt;&lt;p&gt;In distributed systems there is network latency, replication and partitioning,&amp;nbsp; all contributing latency and timing issues to concurrency issues. Consistency approaches concurrency issues taking time and latency into account as well. At the highest consistency level, transactions execute in order of their order of completion (commit) in real time.&amp;nbsp;&lt;/p&gt;&lt;p&gt;Serializable is the strictest isolation level. Strict serializability is the strictest consistency model. In a single node system, there is very little difference between the two because the time issues are small.&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Isolation Levels vs. Consistency Models&lt;/h2&gt;&lt;div&gt;To summarize the key differences.&lt;/div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;&lt;strong data-end=&quot;328&quot; data-start=&quot;308&quot;&gt;Isolation Levels&lt;/strong&gt;&lt;/h3&gt;&lt;p&gt;

&lt;/p&gt;&lt;ul data-end=&quot;804&quot; data-start=&quot;329&quot; style=&quot;text-align: left;&quot;&gt;
&lt;li data-end=&quot;407&quot; data-start=&quot;329&quot;&gt;Prevent read, writes of uncommitted data.&lt;/li&gt;
&lt;li data-end=&quot;481&quot; data-start=&quot;408&quot;&gt;Prevent anomalies like read uncommitted, non repeatable reads, phantom reads&lt;/li&gt;
&lt;li data-end=&quot;593&quot; data-start=&quot;482&quot;&gt;Focus on managing &lt;span data-end=&quot;540&quot; data-start=&quot;519&quot;&gt;concurrent access&lt;/span&gt; to data while balancing performance and correctness.&lt;/li&gt;
&lt;li data-end=&quot;804&quot; data-start=&quot;594&quot;&gt;Common isolation levels (from weakest to strongest):
&lt;ul&gt;
&lt;li data-end=&quot;673&quot; data-start=&quot;651&quot;&gt;&lt;span data-end=&quot;673&quot; data-start=&quot;653&quot;&gt;Read Uncommitted&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;696&quot; data-start=&quot;676&quot;&gt;&lt;span data-end=&quot;696&quot; data-start=&quot;678&quot;&gt;Read Committed&lt;/span&gt;&lt;/li&gt;
&lt;li data-end=&quot;720&quot; data-start=&quot;699&quot;&gt;&lt;span data-end=&quot;720&quot; data-start=&quot;701&quot;&gt;Repeatable Read&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span data-end=&quot;741&quot; data-start=&quot;725&quot;&gt;Serializable&lt;/span&gt; — the strictest standard defined by the &lt;span data-end=&quot;794&quot; data-start=&quot;782&quot;&gt;ANSI SQL&lt;/span&gt; standard.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://khangaonkar.blogspot.com/2010/11/database-isolation.html&quot;&gt;Old blog&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;&lt;strong data-end=&quot;832&quot; data-start=&quot;810&quot;&gt;Consistency Levels&lt;/strong&gt;&lt;/h3&gt;&lt;p&gt;
&lt;/p&gt;&lt;ul data-end=&quot;1217&quot; data-start=&quot;833&quot;&gt;
&lt;li data-end=&quot;877&quot; data-start=&quot;833&quot;&gt;Typically relevant&amp;nbsp;&lt;span data-end=&quot;876&quot; data-start=&quot;853&quot;&gt;distributed databases&lt;/span&gt;.&lt;/li&gt;&lt;li data-end=&quot;877&quot; data-start=&quot;833&quot;&gt;Time is a factor&lt;/li&gt;
&lt;li data-end=&quot;995&quot; data-start=&quot;878&quot;&gt;They describe the &lt;span data-end=&quot;912&quot; data-start=&quot;898&quot;&gt;guarantees&lt;/span&gt; about visibility and ordering of updates in a distributed, replicated data system.&lt;/li&gt;
&lt;li data-end=&quot;1084&quot; data-start=&quot;996&quot;&gt;They focus on the behavior &lt;span data-end=&quot;1049&quot; data-start=&quot;1025&quot;&gt;perceived by clients&lt;/span&gt; across multiple nodes or replicas.&lt;/li&gt;
&lt;li data-end=&quot;1217&quot; data-start=&quot;1085&quot;&gt;Examples include:
&lt;/li&gt;&lt;ul data-end=&quot;1217&quot; data-start=&quot;1107&quot;&gt;
&lt;li data-end=&quot;1133&quot; data-start=&quot;1107&quot;&gt;Strict serializability&lt;/li&gt;
&lt;li data-end=&quot;1217&quot; data-start=&quot;1163&quot;&gt;&lt;span data-end=&quot;1184&quot; data-start=&quot;1165&quot;&gt;Linearizability&lt;/span&gt;&amp;nbsp;&lt;/li&gt;&lt;li data-end=&quot;1217&quot; data-start=&quot;1163&quot;&gt;causal consistency&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;div&gt;&lt;h4 style=&quot;text-align: left;&quot;&gt;&lt;strong data-end=&quot;2205&quot; data-start=&quot;2164&quot;&gt;Example to Illustrate the Difference:&lt;/strong&gt;&lt;/h4&gt;
&lt;h4 data-end=&quot;2221&quot; data-start=&quot;2207&quot;&gt;Scenario:&lt;/h4&gt;
&lt;ul data-end=&quot;2429&quot; data-start=&quot;2222&quot;&gt;
&lt;li data-end=&quot;2290&quot; data-start=&quot;2222&quot;&gt;Two accounts &lt;strong data-end=&quot;2242&quot; data-start=&quot;2237&quot;&gt;A&lt;/strong&gt; and &lt;strong data-end=&quot;2252&quot; data-start=&quot;2247&quot;&gt;B&lt;/strong&gt; initially have a balance of 100 each.&lt;/li&gt;
&lt;li data-end=&quot;2429&quot; data-start=&quot;2291&quot;&gt;Two concurrent transactions:
&lt;ul data-end=&quot;2429&quot; data-start=&quot;2324&quot;&gt;
&lt;li data-end=&quot;2367&quot; data-start=&quot;2324&quot;&gt;&lt;strong data-end=&quot;2333&quot; data-start=&quot;2326&quot;&gt;Tx1&lt;/strong&gt;: Transfer 50 from &lt;strong data-end=&quot;2357&quot; data-start=&quot;2352&quot;&gt;A&lt;/strong&gt; to &lt;strong data-end=&quot;2366&quot; data-start=&quot;2361&quot;&gt;B&lt;/strong&gt;.&lt;/li&gt;
&lt;li data-end=&quot;2429&quot; data-start=&quot;2370&quot;&gt;&lt;strong data-end=&quot;2379&quot; data-start=&quot;2372&quot;&gt;Tx2&lt;/strong&gt;: Reads balances of &lt;strong data-end=&quot;2404&quot; data-start=&quot;2399&quot;&gt;A&lt;/strong&gt; and &lt;strong data-end=&quot;2414&quot; data-start=&quot;2409&quot;&gt;B&lt;/strong&gt; and sums them&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p data-end=&quot;2486&quot; data-start=&quot;2436&quot;&gt;&lt;strong data-end=&quot;2486&quot; data-start=&quot;2436&quot;&gt;Isolation level Serializable:&lt;/strong&gt;&lt;/p&gt;
&lt;ul data-end=&quot;2780&quot; data-start=&quot;2487&quot;&gt;
&lt;li data-end=&quot;2552&quot; data-start=&quot;2487&quot;&gt;Tx1 and Tx2 are serialized, and the sum read by Tx2 is &lt;strong data-end=&quot;2551&quot; data-start=&quot;2544&quot;&gt;200&lt;/strong&gt;.&lt;/li&gt;&lt;li data-end=&quot;2780&quot; data-start=&quot;2553&quot;&gt;(Tx1, Tx2) and (Tx2, Tx1) are valid orders irrespective of when each actually committed first.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-end=&quot;2824&quot; data-start=&quot;2782&quot;&gt;&lt;strong data-end=&quot;2824&quot; data-start=&quot;2782&quot;&gt;Consistency level Strict Serializable&lt;/strong&gt;&lt;/p&gt;
&lt;ul data-end=&quot;2982&quot; data-start=&quot;2825&quot;&gt;
&lt;li data-end=&quot;2909&quot; data-start=&quot;2825&quot;&gt;If &lt;strong data-end=&quot;2837&quot; data-start=&quot;2830&quot;&gt;Tx1&lt;/strong&gt; commits before &lt;strong data-end=&quot;2860&quot; data-start=&quot;2853&quot;&gt;Tx2&lt;/strong&gt; starts, &lt;strong data-end=&quot;2876&quot; data-start=&quot;2869&quot;&gt;Tx2&lt;/strong&gt; must see all effects of &lt;strong data-end=&quot;2908&quot; data-start=&quot;2901&quot;&gt;Tx1&lt;/strong&gt;. The only valid order is (Tx1, Tx2)&lt;/li&gt;&lt;li data-end=&quot;2982&quot; data-start=&quot;2910&quot;&gt;However if there is some overlap like if Tx1 commits after Tx2 starts, then both orders (Tx1, Tx2) and (Tx2, Tx1) can be valid. Reason is that Tx2 cannot read the data committed by Tx1&lt;/li&gt;&lt;/ul&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;A few descriptions&lt;/h3&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Let us briefly touch on some levels you will encounter often. For more detailed descriptions, I will refer you to&amp;nbsp;https://jepsen.io/consistency [1]&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;h4 style=&quot;text-align: left;&quot;&gt;Serializability&lt;/h4&gt;&lt;div&gt;Transactions occur in some total order. Even though they may actually execute concurrently, it appears as if they execute one after another. While serializable will prevent non repeatable reads and phantom reads, It will allow &quot;time travel&quot; anomalies as shown in the example above. It can appear that Tx2 happened before Tx1, even though in reality it was the other way around.&lt;/div&gt;&lt;h4 style=&quot;text-align: left;&quot;&gt;Strict Serializability&lt;/h4&gt;&lt;p&gt;Transactions occur in a strict order that is consistent with the real time (clock time) order in which transactions occur. It applies to the entire system encompassing multiple objects. A is before B in the order if A commits before B begins. So the only valid order is (A,B). However if A commits after B begin, then both orders (A, B) and (B, A) are valid.&amp;nbsp;&lt;/p&gt;&lt;h4 style=&quot;text-align: left;&quot;&gt;Linearizable&lt;/h4&gt;&lt;p&gt;Transactions occur in a strict order that is consistent with the real time (clock time) order in which transactions occur. But this applies to a single object not to the entire dataset. Definition of a single object varies. Could be a key or a table. [1]&lt;/p&gt;&lt;p&gt;Most of the time concurrency issues are important when multiple threads touch the same data and that why this model is also as important as strict serializability.&lt;/p&gt;&lt;h4 style=&quot;text-align: left;&quot;&gt;Causal Consistency&lt;/h4&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;span data-end=&quot;236&quot; data-start=&quot;156&quot;&gt;Transaction that are causally related are seen by all nodes in the same order&lt;/span&gt;, while &lt;span data-end=&quot;313&quot; data-start=&quot;244&quot;&gt;concurrent (unrelated) operations may be seen in different orders&lt;/span&gt;. In a social media application, a user making a post and another user liking the post are causally related. The like must be seen only after the post is seen. However, it is ok for a unrelated post that happened after the previous post to be seen before that.&lt;/div&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Conclusion&lt;/h2&gt;&lt;div style=&quot;text-align: left;&quot;&gt;It is all about how systems behave under concurrency.&amp;nbsp;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;Isolation levels deal with how transactions behave when they run at the same time, while consistency models talk about how different nodes in a distributed system agree on data. And &quot;consistency&quot; itself has changed over time, from enforcing business rules in ACID databases to ensuring replicas don&#39;t drift apart in distributed ones.&amp;nbsp;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;Database vendors advertise the consistency level they support as a key feature. That is why it is important we understand what it means and ensure that we pick the right database the fits our needs.&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;References&amp;nbsp;&lt;/h2&gt;&lt;div style=&quot;text-align: left;&quot;&gt;1. https://jepsen.io/consistency&lt;br /&gt;&lt;br /&gt;2. &lt;a href=&quot;https://fauna.com/blog/demystifying-database-systems-introduction-to-consistency-levels&quot;&gt;Introduction to consistency levels&lt;/a&gt; , Daniel J Abadi&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style=&quot;text-align: left;&quot;&gt;3. &lt;a href=&quot;https://fauna.com/blog/demystifying-database-systems-part-4-isolation-levels-vs-consistency-levels&quot;&gt;Isolation levels vs Consistency levels&lt;/a&gt;, Daniel JAbadi&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/4720722268172114351'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/4720722268172114351'/><link rel='alternate' type='text/html' href='http://khangaonkar.blogspot.com/2025/05/understanding-isolation-levels-vs.html' title=' Understanding Isolation levels vs Consistency levels'/><author><name>Manoj Khangaonkar</name><uri>http://www.blogger.com/profile/13925926828045222969</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-5008017311510568944.post-2548121450692761130</id><published>2025-04-01T18:08:00.000-07:00</published><updated>2025-04-01T18:08:05.906-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Distributed systems"/><category scheme="http://www.blogger.com/atom/ns#" term="Go programming"/><category scheme="http://www.blogger.com/atom/ns#" term="Golang"/><category scheme="http://www.blogger.com/atom/ns#" term="GRPC"/><category scheme="http://www.blogger.com/atom/ns#" term="network programming"/><title type='text'>A non trivial concurrency example in Go language</title><content type='html'>&lt;h1 style=&quot;text-align: left;&quot;&gt;Overview&lt;/h1&gt;&lt;p&gt;In this blog I describe a non trivial concurrency example in the Go programming language. The code is part of my Dynago project&amp;nbsp;&lt;a href=&quot;https://github.com/mdkhanga/dynago&quot;&gt;https://github.com/mdkhanga/dynago&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;This is not a tutorial on Go or on writing concurrent programs. But if you know a little bit of Go and/or little bit of concurrent programing, then I am hoping this can be a useful example.&amp;nbsp;&lt;/p&gt;&lt;p&gt;In Dynago, I have so far built a leaderless cluster of peer servers. The command for starting a server needs to only point to one other server. The only exception is the first server, which has nothing to point to.&amp;nbsp; The servers exchange ping and gossip like messages to share the details on the members of the cluster. After a few messages every server has the cluster membership list. When servers join or leave the cluster, the membership list is updated.&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Code&lt;/h2&gt;&lt;p&gt;If you would like to skip reading the text and jump to the code, the relevant files are :&lt;/p&gt;&lt;p&gt;https://github.com/mdkhanga/dynago/blob/main/cluster/peer.go&lt;br /&gt;https://github.com/mdkhanga/dynago/blob/main/cluster/ClusterService.go&lt;/p&gt;&lt;p&gt;The relevant tests are&amp;nbsp;&lt;br /&gt;https://github.com/mdkhanga/dynago/blob/main/e2e-tests/test_membership.py&lt;br /&gt;https://github.com/mdkhanga/dynago/blob/main/e2e-tests/test_membership_chain.py&lt;/p&gt;&lt;p&gt;I show some screenshots of code snippets in this blog for the casual reader. But if you are interested in the code, I recommend directly looking at the code in github.&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Go concurrency recap&lt;/h2&gt;&lt;div&gt;In Go, you write code that executes concurrently by writing Goroutines.&lt;br /&gt;&lt;br /&gt;A goroutine is like a lightweight thread. It is written as a regular function.&lt;br /&gt;&lt;br /&gt;You run a goroutine using the go keyword.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Go provides channels for communicating between goroutines. Channels are a safer way to send and receive data. This is safer than using shared memory as is done in Java, C++. You do not have to use mutexes to avoid memory visibility and race conditions.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The select statement lets the goroutine wait on receiving data on a channels.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Though not recommended, the shared memory approach of&amp;nbsp; data exchange is also supported. You will need to use primitives from the&amp;nbsp;https://pkg.go.dev/sync package to synchronize access to data.&lt;/div&gt;&lt;div&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;The Examples&lt;/h2&gt;&lt;/div&gt;&lt;p&gt;There were two features in Dynago where concurrency is relevant.&lt;/p&gt;&lt;p&gt;1. Each server receives a message on a GRPC stream. It has to process the message and sometimes send a response back. This is the classic producer - consumer problem. Some goroutines produce. Other goroutines consume and do work. I use channels for sharing the messages between producers and consumers.&lt;/p&gt;&lt;p&gt;2. A map stores the list of cluster members.&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;&lt;span&gt;The map is to be updated as servers join or leave the cluster.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span&gt;Periodically we need to iterate over the map and send out our copy of the membership list to others.&lt;/span&gt;&lt;/li&gt;&lt;ul&gt;&lt;li&gt;The receiving server will merge the received list with it own list.&lt;/li&gt;&lt;li&gt;If the receiving servers list is more up to date, then it sends a response to and the original sender has to merge.&lt;/li&gt;&lt;li&gt;timestamps are used to determine who is more recent&lt;/li&gt;&lt;/ul&gt;&lt;/ul&gt;This the case of multiple goroutines reading and writing a data structure concurrently.&amp;nbsp;&lt;h3 style=&quot;text-align: left;&quot;&gt;Example 1: Channels&amp;nbsp;&lt;/h3&gt;&lt;div&gt;The peer struct shown in the code below defines a channel InMessagesChan for incoming messages and a channel OurMessgesChan for outgoing messages.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMts7YNnEK07l40ncOELr1yHPVWFekkWa7x-Q1S2k7QyxwVL-4Aqsuo7dPeUgX1xXY0QKRPdSBkCD6_tHpsuNbafARvA1x8hgZPf7Lik8u76zFo7QbFEkLKcHGExh_SSe6fkIA8D-0D729fhB9WRVLuIKWHgxatBwmQChViExLeIUMPeJ8TV7XtWit9sY/s369/Screenshot%202025-03-09%20at%203.34.00%E2%80%AFPM.png&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;311&quot; data-original-width=&quot;369&quot; height=&quot;360&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMts7YNnEK07l40ncOELr1yHPVWFekkWa7x-Q1S2k7QyxwVL-4Aqsuo7dPeUgX1xXY0QKRPdSBkCD6_tHpsuNbafARvA1x8hgZPf7Lik8u76zFo7QbFEkLKcHGExh_SSe6fkIA8D-0D729fhB9WRVLuIKWHgxatBwmQChViExLeIUMPeJ8TV7XtWit9sY/w427-h360/Screenshot%202025-03-09%20at%203.34.00%E2%80%AFPM.png&quot; width=&quot;427&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;The receiveMessages goroutine method has for loop with following code. It reads a message from a Grpc stream and sends it to the InMessagesChannel.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEglWjRsEcOgS34suRY1iDW_tO2x4M06p2duQmEStOWEcmNDMNwSuubKpOkjc50V4Gj6HRXK_fd1b4T0IcFTpmWoVmfq82FLTwosHf2Afwr3r31-5YfPewdMAgSB-RydHL_NZw9ft5kqpfsyAq3pQT1mPEZb-fvQ7ioOMmVj_QX-KiwSKx_h5zDxUORkW4c/s393/Screenshot%202025-03-09%20at%203.41.02%E2%80%AFPM.png&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;264&quot; data-original-width=&quot;393&quot; height=&quot;264&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEglWjRsEcOgS34suRY1iDW_tO2x4M06p2duQmEStOWEcmNDMNwSuubKpOkjc50V4Gj6HRXK_fd1b4T0IcFTpmWoVmfq82FLTwosHf2Afwr3r31-5YfPewdMAgSB-RydHL_NZw9ft5kqpfsyAq3pQT1mPEZb-fvQ7ioOMmVj_QX-KiwSKx_h5zDxUORkW4c/w407-h264/Screenshot%202025-03-09%20at%203.41.02%E2%80%AFPM.png&quot; width=&quot;407&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;The processMessageLoop goroutine method in peer.go has a loop that takes messages from the InMessagesChannel and processes them.&amp;nbsp; When a response is needed, it processes the message and writes the response that needs to go out to the OutMessagesChannel. The image below is shortened version of the real code.&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgoZh2t-4VPt0tA-o1kggjL7ygyqL-DRWd39UWhZkmHeA9ABPPlHry7Gtp249ygoDFZN-eVJa3b7kqjbKi-AERf0fBt38WITF54kszGBDqKL9fS1jgeIDyIFecUT5WIOsLpRw0bMeyV0T0SQ1fn1EAm6iQkOch3U9l_xbprt6JP9jn126rrpDfy6htilEk/s1362/Screenshot%202025-03-16%20at%204.40.57%E2%80%AFPM.png&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1020&quot; data-original-width=&quot;1362&quot; height=&quot;360&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgoZh2t-4VPt0tA-o1kggjL7ygyqL-DRWd39UWhZkmHeA9ABPPlHry7Gtp249ygoDFZN-eVJa3b7kqjbKi-AERf0fBt38WITF54kszGBDqKL9fS1jgeIDyIFecUT5WIOsLpRw0bMeyV0T0SQ1fn1EAm6iQkOch3U9l_xbprt6JP9jn126rrpDfy6htilEk/w550-h360/Screenshot%202025-03-16%20at%204.40.57%E2%80%AFPM.png&quot; width=&quot;550&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Lastly the sendLoop goroutine method has a for loop that takes messages from the outMessageChannel and writes them to the outbound Grpc stream.&lt;/div&gt;&lt;br /&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9Q-tITSGE1XRsMxWQUXKRkPzAW9VH1cGDoYPOCV62QgEzWdHFBp06kujD76csfTi31cZr2__rc-n8npk7PU32DOharrlSTuNyMmghd_YL01ZJ7YmbNh8AVypDYVmBGFUrZAgqKZxWfL90cAlPREHumR2a83fVGARUgPRMWfm6xb_1f2wKPpKAwGRS7Ws/s1346/Screenshot%202025-03-15%20at%2012.30.53%E2%80%AFPM.png&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1056&quot; data-original-width=&quot;1346&quot; height=&quot;479&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9Q-tITSGE1XRsMxWQUXKRkPzAW9VH1cGDoYPOCV62QgEzWdHFBp06kujD76csfTi31cZr2__rc-n8npk7PU32DOharrlSTuNyMmghd_YL01ZJ7YmbNh8AVypDYVmBGFUrZAgqKZxWfL90cAlPREHumR2a83fVGARUgPRMWfm6xb_1f2wKPpKAwGRS7Ws/w577-h479/Screenshot%202025-03-15%20at%2012.30.53%E2%80%AFPM.png&quot; width=&quot;577&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;As you can see, very simple. 3 goroutines working concurrently by exchange data over channels. No locking, no synchronization an fewer problems.&lt;br /&gt;&lt;br /&gt;In my opinion, channels is one of the best features of Go.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Example 2 : Shared memory&lt;/h3&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I have this struct cluster which has the list of peers in the cluster&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj9ctce8ikzi6cJGbD8QJwZqkGI4BJ1URHWovimTw-9SxxqEbsAYb9xmSr__nAUYFDynxASJVR4_1bpIqkrV7bH92CuHJ4saqkVNE5hu6d5c0K4d-wTu3U3U1paxAq27hcFOav3wMRc1ykbTe83NsTzGMDTVEQ9DF13f4tdZWVgF53PgCQ4l_-4rt_vmNU/s578/Screenshot%202025-03-10%20at%204.29.58%E2%80%AFPM.png&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;168&quot; data-original-width=&quot;578&quot; height=&quot;134&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj9ctce8ikzi6cJGbD8QJwZqkGI4BJ1URHWovimTw-9SxxqEbsAYb9xmSr__nAUYFDynxASJVR4_1bpIqkrV7bH92CuHJ4saqkVNE5hu6d5c0K4d-wTu3U3U1paxAq27hcFOav3wMRc1ykbTe83NsTzGMDTVEQ9DF13f4tdZWVgF53PgCQ4l_-4rt_vmNU/w461-h134/Screenshot%202025-03-10%20at%204.29.58%E2%80%AFPM.png&quot; width=&quot;461&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;We need to add /update / remove entries from the map in a thread safe manner. The code below uses the mutex to synchronize access to the map. These methods are either called from both peer.go and cluster.go.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgHzCoUs5Iq2fTxZtMa0xgqHvxgH9tg1ku0LHr0uP5iekGtx0H86ZxcFCxFSqm-OOlvqBopuwCHOrErt16LyusdkVMLZnE3r0lNdhplT6kbyCcQBBW1VxR7ifKOzAqD470rLFi6SHkuKRR8IK92dnskk3SUBJMRtl3Zre5dnfDBpuUshuS_ooQtyA62GyE/s1186/Screenshot%202025-03-11%20at%203.53.36%E2%80%AFPM.png&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;926&quot; data-original-width=&quot;1186&quot; height=&quot;392&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgHzCoUs5Iq2fTxZtMa0xgqHvxgH9tg1ku0LHr0uP5iekGtx0H86ZxcFCxFSqm-OOlvqBopuwCHOrErt16LyusdkVMLZnE3r0lNdhplT6kbyCcQBBW1VxR7ifKOzAqD470rLFi6SHkuKRR8IK92dnskk3SUBJMRtl3Zre5dnfDBpuUshuS_ooQtyA62GyE/w537-h392/Screenshot%202025-03-11%20at%203.53.36%E2%80%AFPM.png&quot; width=&quot;537&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The code below shows a loop from the ClusterInfoGossip method that uses the same mutex to synchronize the map while iterating over it. Typically the calls from add, remove and gossip happen from different goroutines. If you do not synchronize, you will have memory visibility problems. Note that since peer is updated, we need to synchronize access to the peer as well.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEifgUYwmtfahycTpz3ChpdN_siG0oK2raw_G1dX42JcmiTSu5XaXRDgeRrYUGm-gWAbMZqVBzUipvawzCrtCBnHzDddbU3f6WVkRwUZMcUZUvq0wOPTfhm1NVeDUtyIJ1cj_QdzlzqMnENSan__U0k8ECpVHLXrEgldgRjRJC6Hfhs44GopYbUVHwcjt4k/s1542/Screenshot%202025-03-11%20at%203.54.20%E2%80%AFPM.png&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1084&quot; data-original-width=&quot;1542&quot; height=&quot;459&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEifgUYwmtfahycTpz3ChpdN_siG0oK2raw_G1dX42JcmiTSu5XaXRDgeRrYUGm-gWAbMZqVBzUipvawzCrtCBnHzDddbU3f6WVkRwUZMcUZUvq0wOPTfhm1NVeDUtyIJ1cj_QdzlzqMnENSan__U0k8ECpVHLXrEgldgRjRJC6Hfhs44GopYbUVHwcjt4k/w587-h459/Screenshot%202025-03-11%20at%203.54.20%E2%80%AFPM.png&quot; width=&quot;587&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;You might be wondering, why can I not just do this using channels when it is safer and cleaner ? What you would do is to write a function with like an event loop.&amp;nbsp; The function can accept commands on a channel and read , update or iterate over the map based on the command. The code would look something like below&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEitUhYezBHxvzVNmTg_tGbp5jOaWOMx71D1jI053rIsGblzXa6MK4rYd88VckJ2SNChKKS3h9rf3PYTT6ozGjLPfG5hm8UbdJdRdfLelM6Hi2-1zGC8IFtBcY_dqyHd58BHZcmrYKP2krA8cLk-oaI4sR_8C0hpYki3GwWQn_aoPd48UdhBXHEpIUnQyJM/s1046/Screenshot%202025-03-11%20at%205.04.30%E2%80%AFPM.png&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;1014&quot; data-original-width=&quot;1046&quot; height=&quot;412&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEitUhYezBHxvzVNmTg_tGbp5jOaWOMx71D1jI053rIsGblzXa6MK4rYd88VckJ2SNChKKS3h9rf3PYTT6ozGjLPfG5hm8UbdJdRdfLelM6Hi2-1zGC8IFtBcY_dqyHd58BHZcmrYKP2krA8cLk-oaI4sR_8C0hpYki3GwWQn_aoPd48UdhBXHEpIUnQyJM/w560-h412/Screenshot%202025-03-11%20at%205.04.30%E2%80%AFPM.png&quot; width=&quot;560&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Which approach you take is sometimes a personal choice. For the message processing, I preferred channels. But for CRUD on a map, I preferred shared memory.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Conclusion&lt;/h2&gt;&lt;div&gt;In conclusion, what I have shown you here is a non trivial concurrency example in Go. Go makes it quite easy to write concurrent programs. The recommended way to share data between goroutines is by channels. By using channels, you can avoid race conditions and memory visibility issues. But the traditional approach of shared memory with synchronization is also supported.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;As I build out Dynago, I plan to blog about the interesting pieces.&amp;nbsp; If you like my blogs, please follow me on &lt;a href=&quot;https://www.linkedin.com/in/manoj-khangaonkar/&quot;&gt;LinkedIn&lt;/a&gt; and/or &lt;a href=&quot;https://x.com/mjkhanga&quot;&gt;X/twitter&lt;/a&gt;.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/2548121450692761130'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/2548121450692761130'/><link rel='alternate' type='text/html' href='http://khangaonkar.blogspot.com/2025/04/a-non-trivial-concurrency-example-in-go.html' title='A non trivial concurrency example in Go language'/><author><name>Manoj Khangaonkar</name><uri>http://www.blogger.com/profile/13925926828045222969</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMts7YNnEK07l40ncOELr1yHPVWFekkWa7x-Q1S2k7QyxwVL-4Aqsuo7dPeUgX1xXY0QKRPdSBkCD6_tHpsuNbafARvA1x8hgZPf7Lik8u76zFo7QbFEkLKcHGExh_SSe6fkIA8D-0D729fhB9WRVLuIKWHgxatBwmQChViExLeIUMPeJ8TV7XtWit9sY/s72-w427-h360-c/Screenshot%202025-03-09%20at%203.34.00%E2%80%AFPM.png" height="72" width="72"/></entry><entry><id>tag:blogger.com,1999:blog-5008017311510568944.post-7912495713219214141</id><published>2025-03-01T08:16:00.000-08:00</published><updated>2025-03-01T08:16:31.568-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="architecture"/><category scheme="http://www.blogger.com/atom/ns#" term="Databases"/><category scheme="http://www.blogger.com/atom/ns#" term="distributed applications"/><category scheme="http://www.blogger.com/atom/ns#" term="Distributed systems"/><title type='text'>Multi Version Concurrency Control (MVCC) in databases</title><content type='html'>&lt;h2 style=&quot;text-align: left;&quot;&gt;Introduction&lt;/h2&gt;&lt;p&gt;Multi version concurrency control (MVCC) is a popular optimistic technique used in modern databases for concurrency control.&lt;/p&gt;&lt;p&gt;MVCC does not use locking. In that regard it is an optimistic technique but distinct from what is know as the optimistic concurrency control (OCC).&lt;/p&gt;&lt;p&gt;MVCC is timestamp based.&lt;/p&gt;&lt;p&gt;MVCC does its concurrency control by keeping multiple copies or versions of each data item. Every transaction sees data only of a specific version , also known as snapshot. Changes made by a transaction will not be seen by others, until the changes are committed.&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Concepts&lt;/h2&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Versioning&lt;/h3&gt;&lt;p&gt;Versioning tuples is at the core of how MVCC does its concurrency control.&lt;/p&gt;&lt;p&gt;Consider the table&amp;nbsp;&lt;/p&gt;&lt;p&gt;id, balance&lt;/p&gt;&lt;p&gt;A, 500&lt;/p&gt;&lt;p&gt;B, 1000&lt;/p&gt;&lt;p&gt;Now logically assume that under the hood, the database adds 2 hidden columns - start time (st) and end time (et)&lt;/p&gt;&lt;p&gt;id, balance, st. , et&lt;/p&gt;&lt;p&gt;A, 500 , 1 , -&lt;/p&gt;&lt;p&gt;B, 1000, 2, -&lt;/p&gt;&lt;p&gt;Let us assume that increasing numbers 1,2 ..... represent time and a - implies infinity or no end time. The version number can be a timestamp or something analogous to a timestamp.&lt;/p&gt;&lt;p&gt;In the above example, we are saying that the first tuple (A, 500) is valid from time 1 to infinity. The second tuple (B, 1000) is valid from 2 to infinity.&lt;/p&gt;&lt;p&gt;With versioning, when a tuple is updated, the original one is unchanged. Instead a new row is added.&lt;/p&gt;&lt;p&gt;Let us say at time 3, A is updated to 550. Logically the table would look like&lt;/p&gt;&lt;p&gt;A, 500 , 1 , 3&lt;/p&gt;&lt;p&gt;A, 550 , 3 , -&lt;/p&gt;&lt;p&gt;B, 1000, 2, -&lt;/p&gt;&lt;p&gt;The latest committed version has an end time of infinity. When a new update for a tuple is committed, the end timestamp of the previous latest is changed to the start timestamp of the new latest.&lt;/p&gt;&lt;p&gt;A transaction reads the latest committed version at the time it starts. A transaction that starts at time 2 and reads A will read 500. But a transaction that starts at 4 and reads A will read 550.&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Snapshot isolation&lt;/h3&gt;&lt;p&gt;Every transaction only reads committed values at the time it starts. It is a snapshot of the database at that time. This referred to as Snapshot isolation.&lt;/p&gt;&lt;p&gt;However it will read any changes that it makes within the scope of its transaction.&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Use cases&lt;/h2&gt;&lt;div&gt;It is easier to understand MVCC with some examples,&lt;/div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Lost update problem&lt;/h3&gt;&lt;div&gt;Let us start with the same table again.&lt;/div&gt;&lt;div&gt;&lt;p&gt;id, balance, st. , et&lt;/p&gt;&lt;p&gt;A, 500 , 1 , -&lt;/p&gt;&lt;/div&gt;&lt;div&gt;At time 2, transaction T1 reads A. The latest version with timestamp (1, -) has value 500. It reads 500.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;At time 3, transaction T2 reads the same row. The latest version is still (1, -) . It too reads 500.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;At time 3, transaction T2 updates the value to 550 and commits. We now have an addition version&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;A, 500 , 1 , 3&lt;/div&gt;&lt;div&gt;A, 550, 3 , -&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;At time 5, T1 updates the value to 600.&amp;nbsp;&lt;/div&gt;&lt;div&gt;At time 6, T1 tries to commit.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;T1s commit is disallowed and it is forced to abort.&amp;nbsp;&lt;/div&gt;&lt;div&gt;The reason is that T1&#39;s snapshot is of time 1. After T1 started, another transaction came along , performed a WRITE on the same record and committed. If we allowed T1 to also commit, it would overwrite T2&#39;s update. This is the lost update problem.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The rule we can deduce here is : The write set of the committing transaction T1 should not intersect with the write set of any transactions that committed after T1 started and before T1 commits.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Write Skew&lt;/h3&gt;&lt;div&gt;Let us start this time with the table having 2 tuples.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;id, balance, st. , et&lt;/div&gt;&lt;div&gt;&lt;p&gt;A, 500 , 1 , -&lt;/p&gt;&lt;p&gt;B, 1000, 2, -&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;At time 1, transaction T1 reads A. The latest version with timestamp (1, -) has value 500. It reads 500.&lt;/div&gt;&lt;div&gt;At time 3 , transaction T1 writes A to 600. But it is still uncommitted.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;A, 500 , 1 , -,&amp;nbsp;&lt;/p&gt;&lt;p&gt;A, 600 , 3 , - , uncommitted&lt;/p&gt;&lt;p&gt;B, 1000, 2, -&lt;/p&gt;&lt;p&gt;At time 4, transaction T2 reads A. The latest committed record is still at timestamp 1. It will read 500 ( not 600 ).&lt;/p&gt;&lt;p&gt;At time 5, T1 commits. The write set to T1 does not intersect with the write set of any other transaction. So it allowed to commit. So we have&lt;/p&gt;&lt;p&gt;A, 500 , 1 , 5,&amp;nbsp;&lt;/p&gt;&lt;p&gt;A, 600 , 5 , - ,&amp;nbsp;&lt;/p&gt;&lt;p&gt;B, 1000, 2, -,&amp;nbsp;&lt;/p&gt;&lt;p&gt;At time 6, T2 updates B to 1200&lt;/p&gt;&lt;p&gt;B, 1200, 6, -, uncommitted&lt;/p&gt;&lt;p&gt;At time 7, T2 tries to commit. It is disallowed and T2 is forced to abort. The reason is that T2 read A which was committed after T2 started. Even though T2 does not update A. It might be using A (that is read earlier) to update say B and that update could be incorrect.This is the write skew problem.&lt;/p&gt;&lt;p&gt;We can thus deduce rule 2: The read set of a committing transaction T should not intersect with the write set of any transaction that has committed after T started.&lt;/p&gt;&lt;/div&gt;&lt;h4 style=&quot;text-align: left;&quot;&gt;Phantom reads&lt;/h4&gt;&lt;div&gt;Let us look at a case where a new record is insert into the table.&lt;/div&gt;&lt;p&gt;id, balance, st. , et&lt;/p&gt;&lt;p&gt;A, 500 , 1 , -&lt;/p&gt;&lt;p&gt;B, 1000, 2, -&lt;/p&gt;&lt;p&gt;At time 3 , transaction T1 runs the query &quot;select sum(balance) from table where balance &amp;gt;=500 &quot;. It gets a result 1500.&lt;/p&gt;&lt;p&gt;At time 4, transaction T2 inserts a new row, (C, 600) and commits&lt;/p&gt;&lt;p&gt;id, balance, st. , et&lt;/p&gt;&lt;p&gt;A, 500 , 1 , -&lt;/p&gt;&lt;p&gt;B, 1000, 2, -&lt;/p&gt;&lt;p&gt;C, 600, 4 , -&lt;/p&gt;&lt;p&gt;At time 5, transaction T1&amp;nbsp; inserts the 1500 value it read into another table.&lt;/p&gt;&lt;p&gt;At time 6, T1 tries to commit. The transaction is not allowed to proceed and aborts.&lt;/p&gt;&lt;p&gt;The reason is that the 1500 value is no longer accurate. At time 4 , another transaction committed a new row and the value should be 2100.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;Rule 3 : If the committing transaction T has predicate queries&amp;nbsp; (where clause ) that depend to number of rows and affected by inserts/deletes, then just before committing the queries need to be run again. The commit is allowed only if the results are the same as before.&lt;/p&gt;&lt;p&gt;Obviously running the queries a second time can be expensive. But that is implementation detail and databases do various optimization.&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Garbage collection&lt;/h2&gt;&lt;p&gt;Maintaining versions for each and every record can take up a lot of disk space. Databases have background process or other method to remove old versions that have no active transaction depending on them.&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Algorithm&lt;/h2&gt;&lt;div&gt;Every tuple is versioned using timestamp or something analogous to it.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Every transaction only reads rows that are committed as of the start time of the transaction. This holds for the entire duration of the transaction. It cannot read any commits that happened after it starts. In other words it sees a &quot;Snapshot&quot; of the data, as of its start time. This is Snapshot isolation.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;When a transaction tries to commit, we check if any other transaction has committed a new version for the rows that we read or write. If there are any such transactions, then we must abort.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Even if our read/write set does not intersect with any other transaction, if our read is impacted by inserts or deletes, we much abort.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;To make this happen, the database needs to maintain start time, end time for every tuple. Not just the latest version, but older versions as well.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;For every tuple that is read or writen, the database needs to know which transactions are active and which committed.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Older versions need to cleaned up when there are no active transactions depending on them.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;For a transaction to be allowed to commit, there rules we discussed apply:&lt;br /&gt;&lt;br /&gt;#1 : Write set of a committing transaction T should not intersect with Write set of any committed transaction that committed after T started.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;#2: The Read set of a committing transaction T should not intersect with the write set of any transaction that has committed after T started.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;#3: When the committing transaction T has predicate queries (WHERE clause, depend on number of rows meeting condition ), then before committing, the queries need to be rerun to ensure that they return the same result.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Some additional rules:&lt;br /&gt;&lt;br /&gt;#4 Write set of a committing transaction T should not intersect with Read set of any transaction that committed after T started.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;#5 If a transaction T updates a row, T should use its own updated version, not the one in older snapshot it started with.&lt;/div&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Commercial Databases supporting MVCC&lt;/h2&gt;&lt;div&gt;Almost all the modern databases we know off support MVCC.&lt;br /&gt;&lt;br /&gt;PostgreSql&lt;/div&gt;&lt;div&gt;Mysql&lt;/div&gt;&lt;div&gt;Oracle&lt;/div&gt;&lt;div&gt;CockroachDB&lt;/div&gt;&lt;div&gt;SqlServer&lt;/div&gt;&lt;div&gt;.... and more&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Advantages of MVCC&lt;/h2&gt;&lt;div&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;Readers do not block readers. Writers do not block readers.&lt;/li&gt;&lt;li&gt;Each transaction works under a consistent snapshot of data.&lt;/li&gt;&lt;li&gt;High concurrency is possible.&lt;/li&gt;&lt;li&gt;Databases can allow querying of old versions, which are known as time travel queries.&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Disadvantages of MVCC&lt;/h2&gt;&lt;div&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;Increased storage requirement because we are storing older version.&lt;/li&gt;&lt;li&gt;Need to do garbage collection.&lt;/li&gt;&lt;li&gt;When conflicts are detect and transactions aborted, applications need to retry.&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Conclusion&lt;/h2&gt;&lt;div&gt;MVCC is a popular concurrency control technique used in many currently popular databases like Postgresql.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;It does not use two phase locking. Unlike OCC, it does not use a staging area. The staging area is built in with versioning.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Like OCC, it works best for workloads where there are few conflicts.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In conclusion, &lt;span data-end=&quot;146&quot; data-start=&quot;102&quot;&gt;Multi-Version Concurrency Control (MVCC)&lt;/span&gt; offers a robust approach to handling &lt;span data-end=&quot;211&quot; data-start=&quot;184&quot;&gt;concurrent transactions&lt;/span&gt; in modern databases by maintaining &lt;span data-end=&quot;276&quot; data-start=&quot;247&quot;&gt;multiple versions of data&lt;/span&gt;.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;While it provides significant benefits such as &lt;span data-end=&quot;349&quot; data-start=&quot;325&quot;&gt;high read throughput&lt;/span&gt;, &lt;span data-end=&quot;373&quot; data-start=&quot;351&quot;&gt;reduced contention&lt;/span&gt;, and &lt;span data-end=&quot;401&quot; data-start=&quot;379&quot;&gt;snapshot isolation&lt;/span&gt;, it also introduces &lt;span data-end=&quot;436&quot; data-start=&quot;422&quot;&gt;complexity&lt;/span&gt; in &lt;span data-end=&quot;462&quot; data-start=&quot;440&quot;&gt;garbage collection&lt;/span&gt; and &lt;span data-end=&quot;489&quot; data-start=&quot;467&quot;&gt;storage management&lt;/span&gt;.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Modern distributed databases combine versioning with clock time to provide strict serializability even in&amp;nbsp; distributed databases without the use of locking. But that is a topic for another blog.&lt;/div&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;References&lt;/h2&gt;&lt;div&gt;Prof. Jen Dittrich Youtube videos&lt;/div&gt;&lt;div&gt;Andy Pavlov CMU Database Youtube video&lt;/div&gt;&lt;div&gt;Database Internals by Alex Petrov&lt;/div&gt;&lt;div&gt;Database Management Systems by Ramakrishnan &amp;amp; Gehrke&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/7912495713219214141'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/7912495713219214141'/><link rel='alternate' type='text/html' href='http://khangaonkar.blogspot.com/2025/03/multi-version-concurrency-control-mvcc.html' title='Multi Version Concurrency Control (MVCC) in databases'/><author><name>Manoj Khangaonkar</name><uri>http://www.blogger.com/profile/13925926828045222969</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-5008017311510568944.post-1441646343576151224</id><published>2025-02-01T07:37:00.000-08:00</published><updated>2025-02-01T07:37:50.158-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Concurrency control"/><category scheme="http://www.blogger.com/atom/ns#" term="Databases"/><category scheme="http://www.blogger.com/atom/ns#" term="Distributed systems"/><title type='text'>Optimistic Concurrency Control In Databases</title><content type='html'>&lt;h2 style=&quot;text-align: left;&quot;&gt;Introduction&lt;/h2&gt;&lt;div style=&quot;text-align: left;&quot;&gt;Optimistic concurrency control refers to concurrency control in databases without the use of explicit locks. Traditionally it is done using locks.&amp;nbsp; But locking reduces throughput and scalability. Modern databases therefore prefer not to use locking and use optimistic techniques. This is a brief description of the optimistic technique.&lt;/div&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Why concurrency control ?&lt;/h2&gt;&lt;p&gt;Concurrency control in databases refers to techniques used to execute transactions concurrently.&lt;/p&gt;&lt;p&gt;From a end users standpoint, every transaction needs to execute correctly and preserve the consistency of the database, irrespective of what other transactions are doing. For the user, it should appear that the transactions are executing in some serial order.&lt;/p&gt;&lt;p&gt;When multiple transactions read and/or write the same data several kinds of errors can happen such as&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;dirty reads - reading uncommitted data&lt;/li&gt;&lt;li&gt;non repeatable reads - read a second time and you get a different value&lt;/li&gt;&lt;li&gt;phantom reads - read a second time and you get additional rows&lt;/li&gt;&lt;li&gt;Lost update - one transaction overwrites others write&lt;/li&gt;&lt;li&gt;dirty write - a transaction reads an uncommitted value and updates it&lt;/li&gt;&lt;li&gt;write skew - the combination of two transactions breaks some invariant like minimum balance&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;At a user level, databases give the users a knob called the isolation level to protect against some of these errors. For an explanation of&amp;nbsp;&lt;a href=&quot;https://khangaonkar.blogspot.com/2010/11/database-isolation.html&quot;&gt;isolation levels&lt;/a&gt;, see the blog I wrote many a few years ago.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;But under the hood databases implement concurrency control to ensure that users see a consistent view of the data at all times.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;There are 3 main types of concurrency control:&lt;/div&gt;&lt;div&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;Pessimistic concurrency control&lt;/li&gt;&lt;li&gt;Optimistic concurrency control (OCC)&amp;nbsp;&lt;/li&gt;&lt;li&gt;Multi version concurrency control (MVCC)&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;The pessimistic approach involves locking resources - rows, tables , pages etc while they are used by one transactions. Other transaction that need that resource wait until the lock is released. This approach prevents conflicts but comes at the cost of throughput.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;OCC and MVCC take the optimistic approach that conflicts rarely happen. They lets transactions proceed without acquiring locks. If a conflict is detect downstream, one of the transaction is forced to abort.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In this blog we discuss one such concurrency control technique - optimistic concurrency control. I will discuss the other two in subsequent blogs.&lt;/div&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;What is optimistic concurrency control ?&lt;/h2&gt;&lt;div&gt;As the name suggests, the approach is optimistic. Optimistic implies a positive view of things that most transactions do not conflict with other and there is no need to slow things down by acquiring locks and holding on to them.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Assumptions are that&lt;/div&gt;&lt;div&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;Transactions are short lived&lt;/li&gt;&lt;li&gt;They rarely conflict&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;The concurrency control here is done in 3 stages.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In the first stage (read) the transaction executes its operations in a private or staging area. Rows that are read known as the read set and rows that are updated known as the write set are identified.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In the second stage (validation) the operations are checked for conflicts. Rows that are being read by one transaction should not be be updated by others. If a conflict is detected, then the transaction is aborted and the staging area cleared&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In the third stage, if there are no conflicts the changes are committed.&lt;/div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;Detecting the conflicts&lt;/h3&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Given 2 transactions A and B,&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;A and B are given timestamps(TS) before the validation starts.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Let assume TS (A)&amp;nbsp; &amp;lt; TS (B).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;If A committed before B started read, then there are no issues and B is also allowed to commit.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;If A committed after B started read but before B started write, then both can commit if the write set of A does not intersect with the read set of B.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;If A read completes before B read completes, and the write set of B does not intersect with both the read set of A and write set of A, then both can commit.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In all the 3 cases, A and B are operating on different records and do not conflict.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In this example, we are looking &quot;backward&quot; from B. If there is a conflict B will asked to abort. Some databases look &quot;forward&quot; but the concept is the same.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;When a validation is being done, no other transaction is allowed to commit. Only one transaction is validated at a time.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Timestamp based&lt;/h2&gt;&lt;div&gt;In pessimistic concurrency control, transactions are ordered in the order in which locks are acquired.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In optimistic concurrency control, transactions are ordered based on timestamps given to transactions.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Commercial databases using OCC&lt;/h2&gt;&lt;div&gt;Some example are:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Amazon Aurora DSQL&lt;/div&gt;&lt;div&gt;Yugabyte&lt;/div&gt;&lt;div&gt;CockroachDB&lt;/div&gt;&lt;div&gt;FoundationDB&lt;/div&gt;&lt;div&gt;Fauna&lt;/div&gt;&lt;div&gt;Google Spanner&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Advantages of OCC&lt;/h2&gt;&lt;div&gt;Higher scalability&lt;/div&gt;&lt;div&gt;Higher throughput&lt;/div&gt;&lt;div&gt;No deadlocks&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In general this is suitable for low conflict data access.&lt;/div&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Disadvantages&lt;/h2&gt;&lt;div&gt;Transactions might need to be aborted.&lt;/div&gt;&lt;div&gt;Applications need to handle aborted transactions and retry.&lt;/div&gt;&lt;div&gt;This might not be suitable for cases when conflict is high - that is multiple transactions needs to access the same data.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Conclusion&lt;/h2&gt;&lt;div&gt;Optimistic concurrency control is becoming increasing popular is modern databases that need to scale and can be distributed across multiple node. It really works well in low data contention scenarios. It is ideal for distributed databases where the locking approach becomes even more expensive when you need to lock across nodes - as would be needed in modern distributed databases. For reasons mentioned above, distributed , cloud native, high concurrency systems are moving towards OCC.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;References:&lt;/h2&gt;1. Database Management Systems - Ramakrishnan and Gehrke&lt;/div&gt;&lt;div&gt;2. Database Internals - Petrov&lt;/div&gt;&lt;div&gt;3. Andy Pavlov CMU lecture on youtube&lt;/div&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/1441646343576151224'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/1441646343576151224'/><link rel='alternate' type='text/html' href='http://khangaonkar.blogspot.com/2025/02/optimistic-concurrency-control-in.html' title='Optimistic Concurrency Control In Databases'/><author><name>Manoj Khangaonkar</name><uri>http://www.blogger.com/profile/13925926828045222969</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-5008017311510568944.post-2893860396877507332</id><published>2025-01-04T15:02:00.000-08:00</published><updated>2025-01-26T09:16:01.739-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Cassandra"/><category scheme="http://www.blogger.com/atom/ns#" term="Distributed systems"/><category scheme="http://www.blogger.com/atom/ns#" term="DynamoDB"/><category scheme="http://www.blogger.com/atom/ns#" term="Go programming"/><category scheme="http://www.blogger.com/atom/ns#" term="Golang"/><category scheme="http://www.blogger.com/atom/ns#" term="GRPC"/><category scheme="http://www.blogger.com/atom/ns#" term="High Availability"/><category scheme="http://www.blogger.com/atom/ns#" term="NoSql"/><category scheme="http://www.blogger.com/atom/ns#" term="Protobuf"/><title type='text'>Dynago : A highly available distributed key value store</title><content type='html'>&lt;h2 style=&quot;text-align: left;&quot;&gt;Introduction&lt;/h2&gt;&lt;p&gt;Dynago is my&amp;nbsp; hobby project to implement a minimal implementation of the distributed system described the paper Dynamo : Amazons highly available key-value store.&lt;/p&gt;&lt;p&gt;This is the first in a series of blogs when I describe how I go about building this and post updates on the progress.&lt;/p&gt;&lt;p&gt;The is my way of&amp;nbsp; &quot;#buildinpublic&quot;.&lt;/p&gt;&lt;p&gt;The github repository is at&amp;nbsp;https://github.com/mdkhanga/dynago&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Why am I doing it ?&lt;/h2&gt;&lt;p&gt;The primary goal is learning. Learning not just for me but also for others who may read this blog and review the code.&lt;/p&gt;&lt;p&gt;A secondary goal is build something useful.&amp;nbsp; Perhaps I may use it somewhere or someone else might.&amp;nbsp;&lt;/p&gt;&lt;p&gt;It is possible that parts of the code might be reusable in another project.&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Why a Dynamo clone ?&lt;/h2&gt;&lt;p&gt;The Dynamo paper is the first paper that got me interested in distributed systems. It has been by desire to build something like that for a long time.&lt;/p&gt;&lt;p&gt;In the real world, DynamoDB is Amazon highly available key/value database. Apache Cassandra is also a project inspired by this paper.&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Tech stack&lt;/h2&gt;&lt;p&gt;I pick Go as the programming language. Why Go ? I have already done a couple of complex projects in Java (B+tree,&amp;nbsp; Raft protocol). So I was not interested in doing it in Java. I am not yet that familiar with Rust. And C/C++ felt like going back in time. So this is an opportunity to do a complex project in Go.&lt;br /&gt;&lt;br /&gt;For network communication / RPC, I use GRPC instead of&amp;nbsp; programming to sockets. My &lt;a href=&quot;https://github.com/mdkhanga/mjraft&quot; target=&quot;_blank&quot;&gt;Raft&lt;/a&gt; implementation is based on sockets. I wanted to try Grpc. If it does not work out, I can switch to sockets.&lt;/p&gt;&lt;p&gt;Storage engine is TBD. Early versions will be in-memory. In the future Rocksdb or another embeddable store is a possibility.&lt;/p&gt;&lt;p&gt;Some technologies we might learn here:&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;&lt;ul style=&quot;text-align: left;&quot;&gt;&lt;li&gt;Go programming&lt;/li&gt;&lt;li&gt;Distributed systems&amp;nbsp;&lt;/li&gt;&lt;li&gt;Databases&lt;/li&gt;&lt;li&gt;Network programming&lt;/li&gt;&lt;li&gt;Programming servers from scratch&lt;/li&gt;&lt;li&gt;Concurrency&lt;/li&gt;&lt;li&gt;High availability&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Implementation plan&lt;/h2&gt;&lt;div&gt;I plan to implement in the following tentative order:&lt;/div&gt;&lt;p&gt;1. Cluster membership&lt;/p&gt;&lt;p&gt;Be able to start multiple servers connect and form a cluster. Gossip to detects servers joining and leaving the cluster.&lt;/p&gt;&lt;p&gt;2. Client API&lt;/p&gt;&lt;p&gt;Simple Get and Put API&lt;/p&gt;&lt;p&gt;Replicate to all nodes.&lt;/p&gt;&lt;p&gt;3. Partitioning using consistent hashing&lt;/p&gt;&lt;p&gt;4. Partitioning with replicas&lt;/p&gt;&lt;p&gt;5. Quorum based read / writes&lt;/p&gt;&lt;p&gt;6. Versioning&lt;/p&gt;&lt;p&gt;7. Hinted handoff&lt;/p&gt;&lt;p&gt;...... and so on&lt;/p&gt;&lt;h2 style=&quot;text-align: left;&quot;&gt;Conclusion&lt;/h2&gt;&lt;div&gt;If you are interested in distributed systems and/or databases, I invite you to follow along.&lt;/div&gt;&lt;p&gt;I am open to suggestions and discussion. If you know more, I am happy to learn from you.&lt;/p&gt;&lt;p&gt;If you like what I am working on, please follow me on&lt;a href=&quot;https://x.com/mjkhanga&quot;&gt; twitter/X&lt;/a&gt; or &lt;a href=&quot;https://www.linkedin.com/in/manoj-khangaonkar/&quot;&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/2893860396877507332'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/2893860396877507332'/><link rel='alternate' type='text/html' href='http://khangaonkar.blogspot.com/2025/01/dynago-highly-available-distributed-key.html' title='Dynago : A highly available distributed key value store'/><author><name>Manoj Khangaonkar</name><uri>http://www.blogger.com/profile/13925926828045222969</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-5008017311510568944.post-3454401949994328637</id><published>2024-07-20T16:27:00.000-07:00</published><updated>2024-07-27T14:34:09.740-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="architecture"/><category scheme="http://www.blogger.com/atom/ns#" term="Databases"/><category scheme="http://www.blogger.com/atom/ns#" term="Distributed systems"/><category scheme="http://www.blogger.com/atom/ns#" term="replication"/><title type='text'>Replication in modern data systems</title><content type='html'>&lt;h2 style=&quot;text-align: left;&quot;&gt;Overview&lt;/h2&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;1hqwu124&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Replication means making a copy of the data for future use in the case of failures or may be to scale.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;g99rr15583&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;bcw5y15608&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Why is it a big deal ? We copy files for backup all the time. For static files, that do not change, making a copy is 1 copy command. But if the data is being updated by users all the time. How often do you run the command to copy. How do you keep the copy in sync with the source ?&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;3u6z6156&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;vlye7158&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;That is the problem of replication in databases and data systems.  All databases have replication built in that you can setup with a command or two.  So why read or discuss it? If you are building a distributed systems that involves data,  you will need to replicate data. The concepts from databases will be useful.&amp;nbsp;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;vlye7158&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;While replication is most well known for its use with databases, it is also a critical part of distributed systems where the data is unstructured such as distributed file systems (HDFS) or messaging systems (Apache Kafka)&amp;nbsp;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;vlye7158&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;This post covers replication in traditional single node systems as well as modern distributed systems.&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5A-bmCXYSk4S8FCJYWYKD0FCqVEGIZSnNlsrChInm_ZKaFH0b7GopxzDL_ycx1oRDPRfuHl8Vz7ml_wBPTOlJS_O07965shtXl3lvGFwuVbobQqSgqirYIWQW3ppoAreFpEOKjy0N78zjQvTITk_IYlz7bfq89AaFcyrBu_eeGKlQCJMew9lOwEXgfUE/s960/leader%20based%20replication.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;720&quot; data-original-width=&quot;960&quot; height=&quot;338&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5A-bmCXYSk4S8FCJYWYKD0FCqVEGIZSnNlsrChInm_ZKaFH0b7GopxzDL_ycx1oRDPRfuHl8Vz7ml_wBPTOlJS_O07965shtXl3lvGFwuVbobQqSgqirYIWQW3ppoAreFpEOKjy0N78zjQvTITk_IYlz7bfq89AaFcyrBu_eeGKlQCJMew9lOwEXgfUE/w552-h338/leader%20based%20replication.jpg&quot; width=&quot;552&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;vlye7158&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;r5iof246175&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;ptsdx246519&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;image-component data-node-attrs=&quot;{&amp;quot;id&amp;quot;:&amp;quot;qpyab246518&amp;quot;,&amp;quot;containerData&amp;quot;:{&amp;quot;width&amp;quot;:{&amp;quot;size&amp;quot;:&amp;quot;CONTENT&amp;quot;},&amp;quot;alignment&amp;quot;:&amp;quot;CENTER&amp;quot;,&amp;quot;textWrap&amp;quot;:true},&amp;quot;image&amp;quot;:{&amp;quot;src&amp;quot;:{&amp;quot;id&amp;quot;:&amp;quot;eeeb94_a3acc0ab7e764b5d8f9274c37d57b1db~mv2.jpg&amp;quot;},&amp;quot;width&amp;quot;:960,&amp;quot;height&amp;quot;:720},&amp;quot;link&amp;quot;:null,&amp;quot;disableExpand&amp;quot;:null,&amp;quot;altText&amp;quot;:null,&amp;quot;caption&amp;quot;:null,&amp;quot;disableDownload&amp;quot;:null,&amp;quot;loading&amp;quot;:false,&amp;quot;loadingPercentage&amp;quot;:null,&amp;quot;tempData&amp;quot;:null}&quot;&gt;&lt;/image-component&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;kppt5246520&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;4282n247&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;h2 class=&quot;hQtSd _51IVl&quot; dir=&quot;auto&quot; id=&quot;q25zn250&quot; indentation=&quot;0&quot; level=&quot;2&quot; textstyle=&quot;[object Object]&quot;&gt;Why do we need replication ?&lt;/h2&gt;&lt;div&gt;There are several reasons why replication is needed. It is more than just taking a backup.&lt;/div&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;2nqf9434&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;h3 class=&quot;_07o0J _51IVl&quot; dir=&quot;auto&quot; id=&quot;az3qj438&quot; indentation=&quot;0&quot; level=&quot;3&quot; textstyle=&quot;[object Object]&quot;&gt;Redundancy &lt;/h3&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;fw2mj5123&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Make a copy of the data. When the main server becomes unavailable for any reason, switch to the copy. This is ensure that the data is always available.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;e9pei5161&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;h3 class=&quot;_07o0J _51IVl&quot; dir=&quot;auto&quot; id=&quot;w9vsk495&quot; indentation=&quot;0&quot; level=&quot;3&quot; textstyle=&quot;[object Object]&quot;&gt;Scalability&lt;/h3&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;1d4b38868&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Your data becomes really popular and the database gets a lot of read requests and cannot keep up. So you make copies of the database and have a load balancer distribute the request across to the copies (replicas).&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;hi2fg8908&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;h3 class=&quot;_07o0J _51IVl&quot; dir=&quot;auto&quot; id=&quot;lmwz0105&quot; indentation=&quot;0&quot; level=&quot;3&quot; textstyle=&quot;[object Object]&quot;&gt;Geo distribution of data&lt;/h3&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;fq8rz5106&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Bring the data close of user. You have users in Americas, Europe and Asia. Data from americas is replicated to Europe and Asia, so users there can read data locally without making a round trip to the americas for every read.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;jvmfe14355&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;h3 class=&quot;_07o0J _51IVl&quot; dir=&quot;auto&quot; id=&quot;ox6sf14376&quot; indentation=&quot;0&quot; level=&quot;3&quot; textstyle=&quot;[object Object]&quot;&gt;Secondary use cases&lt;/h3&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;gd7el32437&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;bfpqt32520&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;These are lesser known and unconventional use cases. They might be done higher up in the stack at the application layer or middleware than in the database.&amp;nbsp;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;t9o3n14911&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;h4 class=&quot;yfJW- _51IVl&quot; dir=&quot;auto&quot; id=&quot;5nuhe32271&quot; indentation=&quot;0&quot; level=&quot;4&quot; textstyle=&quot;[object Object]&quot;&gt;Mirroring&lt;/h4&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;atr4044227&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;x39c744395&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Mirroring involves replicating the requests to the application to a copy of the entire application stack. You can think of this as application level replication.&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi4frfffQDQ0QJDpTVDm4OvhPjmpSaO0fPr3Dacz60qEdy7JiW48pmkjhyphenhyphen-XdZg_tW78rPscGznuS0XVNbNEVQicTN0OmgXym52Y1UCSRZv2-C5LtBF8hNw6tYxDp9tf1IgB1PAdyb8UZ4H5MyX5KVfDPgoAYmt0I9Z-wL0ca5_AZYL1JRe1mvv0UP887s/s960/Mirroring.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;720&quot; data-original-width=&quot;960&quot; height=&quot;311&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi4frfffQDQ0QJDpTVDm4OvhPjmpSaO0fPr3Dacz60qEdy7JiW48pmkjhyphenhyphen-XdZg_tW78rPscGznuS0XVNbNEVQicTN0OmgXym52Y1UCSRZv2-C5LtBF8hNw6tYxDp9tf1IgB1PAdyb8UZ4H5MyX5KVfDPgoAYmt0I9Z-wL0ca5_AZYL1JRe1mvv0UP887s/w515-h311/Mirroring.jpg&quot; width=&quot;515&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;x39c744395&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;nol2f170510&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;image-component data-node-attrs=&quot;{&amp;quot;id&amp;quot;:&amp;quot;2l4vt170509&amp;quot;,&amp;quot;containerData&amp;quot;:{&amp;quot;width&amp;quot;:{&amp;quot;size&amp;quot;:&amp;quot;CONTENT&amp;quot;},&amp;quot;alignment&amp;quot;:&amp;quot;CENTER&amp;quot;,&amp;quot;textWrap&amp;quot;:true},&amp;quot;image&amp;quot;:{&amp;quot;src&amp;quot;:{&amp;quot;id&amp;quot;:&amp;quot;eeeb94_f4b6aab4658648bea8bb864b54a7d81c~mv2.jpg&amp;quot;},&amp;quot;width&amp;quot;:960,&amp;quot;height&amp;quot;:720},&amp;quot;link&amp;quot;:null,&amp;quot;disableExpand&amp;quot;:null,&amp;quot;altText&amp;quot;:null,&amp;quot;caption&amp;quot;:null,&amp;quot;disableDownload&amp;quot;:null,&amp;quot;loading&amp;quot;:false,&amp;quot;loadingPercentage&amp;quot;:null,&amp;quot;tempData&amp;quot;:null}&quot;&gt;&lt;/image-component&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;1w8kh170511&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;rvrls58004&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;hxk7958089&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;For example, for a REST service, this involves sending the http request, not just to the production service but also to a mirror service. &lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;3cosk73220&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;be7d373306&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;The mirror service reads and writes from the mirror database. Mirror database is a previous replica that was in sync with the leader. Just before starting mirroring, it is discontinued as a replica so it does not get duplicates.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;ei9hj80187&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;gsdah80274&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Mirroring can be used for testing large complex changes against production traffic. &lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;4czim95245&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;lvcjy95333&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Data in the mirror database is then compared with data in the production database for accuracy.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;dg7ya32188&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;h4 class=&quot;yfJW- _51IVl&quot; dir=&quot;auto&quot; id=&quot;vdxk530343&quot; indentation=&quot;0&quot; level=&quot;4&quot; textstyle=&quot;[object Object]&quot;&gt;Testing&lt;/h4&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;zdjvm43313&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;g4oyy104487&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;A regular database replica is used as a test database. Various kinds of tests - feature tests, performance tests, concurrency tests, scalability tests can be run with services running with the replica. This is a different use case from mirroring.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;sg34u43397&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;h4 class=&quot;yfJW- _51IVl&quot; dir=&quot;auto&quot; id=&quot;sfe3m15234&quot; indentation=&quot;0&quot; level=&quot;4&quot; textstyle=&quot;[object Object]&quot;&gt;Migration&lt;/h4&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;yuggh131100&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;dhofk144238&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;This can be used to eliminate or reduce downtimes needed for migration.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;u64pc131190&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Create additional replicas.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;ko4xt135062&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Run migration on them.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;7jo9m137248&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Rollover the application services to the new database replicas.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;o6f65556&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;h2 class=&quot;hQtSd _51IVl&quot; dir=&quot;auto&quot; id=&quot;n3fox27057&quot; indentation=&quot;0&quot; level=&quot;2&quot; textstyle=&quot;[object Object]&quot;&gt;Replication strategies&lt;/h2&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;fakbh28176&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;h3 class=&quot;_07o0J _51IVl&quot; dir=&quot;auto&quot; id=&quot;jbvxo28203&quot; indentation=&quot;0&quot; level=&quot;3&quot; textstyle=&quot;[object Object]&quot;&gt;Single leader &lt;/h3&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;uvf7t8877&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;jtwcq8916&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;This is the most common pattern.  It shown in Figure 1.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;zdpg211105&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;gj4vh11145&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;One server is designated as the leader. The others are followers. All writes go to the leader. The leader replicates the writes to the followers.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;llbyh17546&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;dsyev17587&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;The advantages are :&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;duguv18449&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;qp92y18491&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Setting up is fairly easy.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;16j1519626&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;qnqfh19669&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Reads become scalable. You can put a load balancer in front and distribute read requests to followers.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;uxkb124615&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;nbmcv24659&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;High availability: If the leader fails, you fail over to one of the followers and let it become the leader.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;0515z31967&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;l99i932012&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;The disadvantages are :&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;xoekz33138&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;7m4gy33275&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;All the writes go to one server , the leader. So this can become a bottleneck. Writes are not scaled.&amp;nbsp;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;7m4gy33275&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;If you read from a replica that is behind on replication, you might read stale data.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;b83qq39164&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;h3 class=&quot;_07o0J _51IVl&quot; dir=&quot;auto&quot; id=&quot;dh5mt39211&quot; indentation=&quot;0&quot; level=&quot;3&quot; textstyle=&quot;[object Object]&quot;&gt;Multi leader &lt;/h3&gt;&lt;div&gt;Writes can go to more than one server.&lt;/div&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;q8yve40156&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;7qj0940204&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Multi leader replication is needed when&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;6nind42029&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;t195b42078&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;(1) Writes and replication needs to happen across geographically distributed areas.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;o6ten46593&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;11xzs46643&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;(2) Connectivity to single leader is not guaranteed. The is usually the case with mobile devices or laptops or when people want the ability to work offline and/or multiple devices.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;3ce4t56244&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;iyf9u837&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;image-component data-node-attrs=&quot;{&amp;quot;id&amp;quot;:&amp;quot;opgz6836&amp;quot;,&amp;quot;containerData&amp;quot;:{&amp;quot;width&amp;quot;:{&amp;quot;size&amp;quot;:&amp;quot;CONTENT&amp;quot;},&amp;quot;alignment&amp;quot;:&amp;quot;CENTER&amp;quot;,&amp;quot;textWrap&amp;quot;:true},&amp;quot;image&amp;quot;:{&amp;quot;src&amp;quot;:{&amp;quot;id&amp;quot;:&amp;quot;eeeb94_77683841e6a84d1c9f6cc900bc29cf98~mv2.jpg&amp;quot;},&amp;quot;width&amp;quot;:960,&amp;quot;height&amp;quot;:720},&amp;quot;link&amp;quot;:null,&amp;quot;disableExpand&amp;quot;:null,&amp;quot;altText&amp;quot;:&amp;quot;Multi leader repl&amp;quot;,&amp;quot;caption&amp;quot;:&amp;quot;Multi leader replication&amp;quot;,&amp;quot;disableDownload&amp;quot;:null,&amp;quot;loading&amp;quot;:false,&amp;quot;loadingPercentage&amp;quot;:null,&amp;quot;tempData&amp;quot;:null}&quot;&gt;&lt;/image-component&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;bm75z838&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;tx93756757&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;dl7ky56811&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;In the geo distributed case, the writes go to a local local leader. The local leader not only replicates to local replicas but also to the distributed leader (who replicate to their replicas).&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;qnuqn69448&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;08sia69501&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;In the mobile case, the writes are store locally and the replicated periodically when connectivity is available.&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_wGsLslNSxBVL0jUAdCigw6Y6TsnOmx94BjYJXUL7BwqbN9lDRbqXiobbBR4vERSrWV2EEgCjJ2ETJADmUHb5RydmQyyOVMXyTQ9bP5tZd-veuD2rqwkXQeqXmFJx5RiiVjdDyeZEHvb2UIENMvJX52pySoPkJbHYmGLSxmFLbxvAM1pOxug7vGkYiJ8/s960/Multi%20leader%20replication.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;720&quot; data-original-width=&quot;960&quot; height=&quot;434&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_wGsLslNSxBVL0jUAdCigw6Y6TsnOmx94BjYJXUL7BwqbN9lDRbqXiobbBR4vERSrWV2EEgCjJ2ETJADmUHb5RydmQyyOVMXyTQ9bP5tZd-veuD2rqwkXQeqXmFJx5RiiVjdDyeZEHvb2UIENMvJX52pySoPkJbHYmGLSxmFLbxvAM1pOxug7vGkYiJ8/w552-h434/Multi%20leader%20replication.jpg&quot; width=&quot;552&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;56e3k78647&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;5mgon78703&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Advantages:&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;tt1fb79298&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;ho8f079353&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Writes are also scaled.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;qhmti80784&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;1nxhs80840&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Writes can done locally or close to clients. Better latency for writes.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;iehl185209&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;o40ot85266&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Disadvantages:&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;pyb8h46342&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;qx0x346392&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Since writes happen at multiple leaders. There can be conflict. The conflicts need to be resolved.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;m0bih2501&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;h3 class=&quot;_07o0J _51IVl&quot; dir=&quot;auto&quot; id=&quot;7bq5o2536&quot; indentation=&quot;0&quot; level=&quot;3&quot; textstyle=&quot;[object Object]&quot;&gt;Leaderless&lt;/h3&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;n24m094003&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;crou894062&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;In the leaderless model, all nodes are equal and no node is designated leader. Writes can go to any node and that node replicates the write to other nodes. This is the model made popular by AWS Dynamo and later adopted by Cassandra.&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgoH6eKUE58AL79v9-yM0wsdhRbS0sWF2HH35gYdeBJpACdR-tr9VZ9M0LWDMI2w5rq5nssdLGyjByWLis4OIufexEhH359tMwHMH1VD-X72MZviFuFfusoTIFay8bh_gDA32N0WnyX916ejJyzE6w15vO7EmmKoQcUo20BeRlRH4Zx0mHNgm0viUl2jO4/s960/Leaderless.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;720&quot; data-original-width=&quot;960&quot; height=&quot;292&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgoH6eKUE58AL79v9-yM0wsdhRbS0sWF2HH35gYdeBJpACdR-tr9VZ9M0LWDMI2w5rq5nssdLGyjByWLis4OIufexEhH359tMwHMH1VD-X72MZviFuFfusoTIFay8bh_gDA32N0WnyX916ejJyzE6w15vO7EmmKoQcUo20BeRlRH4Zx0mHNgm0viUl2jO4/w474-h292/Leaderless.jpg&quot; width=&quot;474&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Consensus based replication&lt;/h3&gt;&lt;div&gt;All the above methods have either write conflict or read consistency issues. Raft and Paxos are two well protocols for replicating log entries. Data to be replicated is modeled as a list of entries in log. The short story is that one server sends one entry or a sequence of entries to others and it is considered committed if a majority of servers acknowledge having received them. Raft has leader election but Paxos is leaderless. Raft protocol describes in detail leader election, replication, server crashes, recovery and consistency checks. The paper is a good read for anyone interested in distributed systems.&lt;/div&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;q5nwq32394&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;h45kn254094&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;image-component data-node-attrs=&quot;{&amp;quot;id&amp;quot;:&amp;quot;cu7oa254093&amp;quot;,&amp;quot;containerData&amp;quot;:{&amp;quot;width&amp;quot;:{&amp;quot;custom&amp;quot;:&amp;quot;673&amp;quot;},&amp;quot;alignment&amp;quot;:&amp;quot;CENTER&amp;quot;,&amp;quot;textWrap&amp;quot;:true},&amp;quot;image&amp;quot;:{&amp;quot;src&amp;quot;:{&amp;quot;id&amp;quot;:&amp;quot;eeeb94_a1ceb909b8044874a2ce0bd413db8f14~mv2.jpg&amp;quot;},&amp;quot;width&amp;quot;:960,&amp;quot;height&amp;quot;:720},&amp;quot;link&amp;quot;:null,&amp;quot;disableExpand&amp;quot;:null,&amp;quot;altText&amp;quot;:null,&amp;quot;caption&amp;quot;:null,&amp;quot;disableDownload&amp;quot;:null,&amp;quot;loading&amp;quot;:false,&amp;quot;loadingPercentage&amp;quot;:null,&amp;quot;tempData&amp;quot;:null}&quot;&gt;&lt;/image-component&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;vgjzv254095&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;wstt9253225&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;3rkhd27083&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;zpdn527161&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;h2 class=&quot;hQtSd _51IVl&quot; dir=&quot;auto&quot; id=&quot;3ey8u562&quot; indentation=&quot;0&quot; level=&quot;2&quot; textstyle=&quot;[object Object]&quot;&gt;Replication Implementations&lt;/h2&gt;&lt;div&gt;The first three techniques apply to databases which deal with structured data and are a little more complicated.&lt;/div&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;bo0i826505&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;h3 class=&quot;_07o0J _51IVl&quot; dir=&quot;auto&quot; id=&quot;ydjhf701&quot; indentation=&quot;0&quot; level=&quot;3&quot; textstyle=&quot;[object Object]&quot;&gt;Statement based replication&lt;/h3&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;n9ujg112956&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;8xln9113014&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;In this approach, the SQL statements such as INSERT/UPDATE/DELETE etc are forwarded as they are from the leaders to the followers. While this can work in most cases, it does not work in certain cases such as timestamps or when you generate an id or a random number. &lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;dcxdy968&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;iobdq132103&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;It is not efficient either. If you insert a record and then delete it, why replicate both commands ?&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;jmspw26532&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;h3 class=&quot;_07o0J _51IVl&quot; dir=&quot;auto&quot; id=&quot;j9ozv26558&quot; indentation=&quot;0&quot; level=&quot;3&quot; textstyle=&quot;[object Object]&quot;&gt;Write ahead log (WAL) replication&lt;/h3&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;gon5r138066&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;gi1kb138126&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Databases first append every write to the WAL before doing anything else, before writing it to structured storage from where it will be read.  WAL is used for recovery. If the database crashed, it state is reconstructed from the WAL.  A recent slogan has been &quot;The WAL is the database&quot;. Replication here involves replicating the WAL.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;lu0h2165457&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;ydl4l165518&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;A disadvantage is that WAL entries contain where specific storage details like which byte in which block is to be updated. This can create compatibility issues if the leader and followers are on different versions.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;g1g72112728&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;h3 class=&quot;_07o0J _51IVl&quot; dir=&quot;auto&quot; id=&quot;1t0kx1289&quot; indentation=&quot;0&quot; level=&quot;3&quot; textstyle=&quot;[object Object]&quot;&gt;Logical replication&lt;/h3&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;n3xfj165074&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;anjsh165137&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;A logical log on the other hand captures at a row, how the table was changed. You can view this as an approach somewhere between statement based and WAL replication.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;anjsh165137&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Change data capture is a form of logical replication. It is used to replicate changes in a database to other third party systems. A popular use case is data warehousing where data from multiple sources is aggregated and summarized for analytics.&amp;nbsp;&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Unstructured data replication&lt;/h3&gt;&lt;div&gt;For unstructured data as in distributed file systems the unit for replication is a block of data. Data is first partitioned into blocks and each block is replicated independently.&lt;/div&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;k1yck1470&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;h2 class=&quot;hQtSd _51IVl&quot; dir=&quot;auto&quot; id=&quot;g16ap30166&quot; indentation=&quot;0&quot; level=&quot;2&quot; textstyle=&quot;[object Object]&quot;&gt;Potential issues with replication&lt;/h2&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;8f8sh30195&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;h3 class=&quot;_07o0J _51IVl&quot; dir=&quot;auto&quot; id=&quot;za8qx1480&quot; indentation=&quot;0&quot; level=&quot;3&quot; textstyle=&quot;[object Object]&quot;&gt;Replication Lag&lt;/h3&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;omm9q201903&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;5qjap201966&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Most of the time replication is asynchronous.  Client writes to the leader and returns before any acknowledgement that it has been replicated. Synchronous replication is not viable due both performance and availability issues. A single failure can hold up all replications.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;2733u1691&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;xlr5p235231&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Lost write&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;3bikl1702&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;However, one problem this creates is that if you read immediately after a write, the replica you are reading from may not yet have your last write.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;oh3qm2176&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;aeh77235937&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Inconsistent read&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;fj9jo237433&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;If you read multiple times in quick succession ( same read) , each read may get a different result depending on which replica services the read ( as the replicas may be in different stages of replication)&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;fbmk9254924&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;mdpue254991&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Cassandra addressed this issue using quorum. CockroachDb uses a consensus protocol like Raft.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;9a0zm262228&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;h3 class=&quot;_07o0J _51IVl&quot; dir=&quot;auto&quot; id=&quot;q135m262296&quot; indentation=&quot;0&quot; level=&quot;3&quot; textstyle=&quot;[object Object]&quot;&gt;Write Conflicts&lt;/h3&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;xkirb263452&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;zca5a263521&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Write conflict is an issue in multi leader replication. This happens when multiple clients update the same data while talking to a different master. The database does not know which update to accept and how they should be merged. This is similar to a merge conflict in git.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;duk442188&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;	&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;n5cyy285738&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;An approach to handle conflicts is to store both versions on write. But on read, send both versions to the client and let the the client resolve the conflict&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;ryq4y2658&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;h2 class=&quot;hQtSd _51IVl&quot; dir=&quot;auto&quot; id=&quot;9mywl30871&quot; indentation=&quot;0&quot; level=&quot;2&quot; textstyle=&quot;[object Object]&quot;&gt;Replication is real world systems&lt;/h2&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;mozjp4374&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;jij99300705&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;The product documentation for database on replication can be quite confusing. It best to follow a tutorial or blog in the internet.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;b984c300638&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;h3 class=&quot;_07o0J _51IVl&quot; dir=&quot;auto&quot; id=&quot;bcmz230901&quot; indentation=&quot;0&quot; level=&quot;3&quot; textstyle=&quot;[object Object]&quot;&gt;Postgres&lt;/h3&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;1bmxg170782&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;64khh170876&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;The documentation and blogs describe it in 2 ways.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;sietx200262&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;rvzjf176987&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;You can set it up as synchronous, asynchronous , streaming , log file based etc&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;oweig31262&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;rp301310489&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;And it can be  WAL based or  logical replication. Statement based is rarely seen.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;2tgt55913&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;2b1uc5985&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;In snapshot replication, a snapshot of the database is taken and replicated to followers.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;oomxu13764&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Instead of streaming, you can also setup the replication as file based, where the WAL files are periodically shipped to followers.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;w2fs8216035&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;lo14r216130&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;In WAL replication, replication slots lets the leader track how much of the WAL is replicated to each replica. This helps the leader not discard segments not yet replicated. But this consumes resources on the leader. Replication slots need to be managed and deleted when not needed.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;hapca32016&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;h3 class=&quot;_07o0J _51IVl&quot; dir=&quot;auto&quot; id=&quot;w9n3h32093&quot; indentation=&quot;0&quot; level=&quot;3&quot; textstyle=&quot;[object Object]&quot;&gt;Mysql&lt;/h3&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;tgjb632544&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;0m57x32620&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;The traditional way in mysql was a logical replication based on their binlog file - a binary format for logical changes.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;rkzmx44401&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;tmuuy44478&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;The newer way is based on global transaction identifier (GTID) which is built on top of the binlog. It can be either statement based or row based.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;qeyau31293&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;h3 class=&quot;_07o0J _51IVl&quot; dir=&quot;auto&quot; id=&quot;ir2fx31324&quot; indentation=&quot;0&quot; level=&quot;3&quot; textstyle=&quot;[object Object]&quot;&gt;Dynamo / Cassandra&lt;/h3&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;ziwov243&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;99qso323&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;In this architecture, replication is fundamental to the architecture. All you need to do is to set the replication factor to greater than 1. All servers are equal - no leader and no follower.  Writes can go to any server. Partitioning is also fundamental to the architecture. The server that receives the write redirects the write to appropriate server. From here it is replicated to other servers based on the replication factor.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;xzcnb58408&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;9beci58489&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Consistency issues are addressed using quorum based tunable consistency. Quorum mean a majority which is (RF/2+1) agree on something. If you have replication factor (RF) 3, quorum is 2. So on a write, at least 2 nodes need to acknowledge that the write was saved. On read, at least 2 nodes need to agree on the return value. In general, to avoid inconsistencies,  you want Read quorum (R)+ Write quorum (W) &amp;gt; RF .&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;2yd1r4809&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;h3 class=&quot;_07o0J _51IVl&quot; dir=&quot;auto&quot; id=&quot;od3jo4846&quot; indentation=&quot;0&quot; level=&quot;3&quot; textstyle=&quot;[object Object]&quot;&gt;CockroachDb&lt;/h3&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;wcwre105150&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;racpm105232&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;CockroachDB uses the Raft distributed consensus protocol to ensure that a majority of replicas are in consensus before any change is committed. This is the safest approach to ensure consistency but comes at a cost.&lt;/p&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Apache Kafka&lt;/h3&gt;&lt;div&gt;In Kafka, messages are sent and received from topics. Topics are split into partition. Each partition has one leader and a configurable number of replicas. Writes go to the leader which replicates to the replicas. Reads can go to the replicas. Each broker is a leader for some partitions but a follower for other partitions. Like Cassandra and CockroachDb, replication is core to the architecture and easy to setup.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;h3 style=&quot;text-align: left;&quot;&gt;Apache Hadoop (HDFS)&lt;/h3&gt;&lt;div&gt;This applies to any distributed file system. The file is a sequence of blocks of data. HDFS has a name node and data nodes. Name node maintains a map of which data nodes have the blocks of a file. Each block is replicated to a configurable number of data nodes.&lt;/div&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;fpp615587&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;h3 class=&quot;_07o0J _51IVl&quot; dir=&quot;auto&quot; id=&quot;e1ef55625&quot; indentation=&quot;0&quot; level=&quot;3&quot; textstyle=&quot;[object Object]&quot;&gt;Conclusion&lt;/h3&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;c4yzi285&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;576u5368&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Replication is a critical piece of any distributed data system. It has to be part of the core architecture. It cannot come after the fact like it did in the past. While redundancy and HA are well known benefits, there are other benefits such geo distribution of data as well. It can cause some effects such as read consistency. Care should be taken to address those. Different products use different strategies. You should be familiar with the replication strategies, configuration and side effects for your data product. If you are building a new system with data, understanding how existing systems replicate and the issues they face, can help you design your replication.&lt;/p&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/3454401949994328637'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/3454401949994328637'/><link rel='alternate' type='text/html' href='http://khangaonkar.blogspot.com/2024/07/replication-in-modern-data-systems.html' title='Replication in modern data systems'/><author><name>Manoj Khangaonkar</name><uri>http://www.blogger.com/profile/13925926828045222969</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5A-bmCXYSk4S8FCJYWYKD0FCqVEGIZSnNlsrChInm_ZKaFH0b7GopxzDL_ycx1oRDPRfuHl8Vz7ml_wBPTOlJS_O07965shtXl3lvGFwuVbobQqSgqirYIWQW3ppoAreFpEOKjy0N78zjQvTITk_IYlz7bfq89AaFcyrBu_eeGKlQCJMew9lOwEXgfUE/s72-w552-h338-c/leader%20based%20replication.jpg" height="72" width="72"/></entry><entry><id>tag:blogger.com,1999:blog-5008017311510568944.post-7271596687817394343</id><published>2024-07-03T17:12:00.000-07:00</published><updated>2024-07-07T10:43:13.363-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="BigData"/><category scheme="http://www.blogger.com/atom/ns#" term="Cassandra"/><category scheme="http://www.blogger.com/atom/ns#" term="CockRoachDB"/><category scheme="http://www.blogger.com/atom/ns#" term="Databases"/><category scheme="http://www.blogger.com/atom/ns#" term="Distributed systems"/><category scheme="http://www.blogger.com/atom/ns#" term="Partitioning"/><title type='text'>How modern distributed systems scale by partitioning ?</title><content type='html'>&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 class=&quot;hQtSd _51IVl&quot; data-pm-slice=&quot;1 1 []&quot; dir=&quot;auto&quot; id=&quot;foo&quot; indentation=&quot;0&quot; level=&quot;2&quot; textstyle=&quot;[object Object]&quot;&gt;1.0 Introduction&lt;/h2&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;1ob8i&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;In the last 20 years, software systems moved to the internet and handled large volumes of data and millions of requests. Most people interact with these systems using a browser or a mobile device. At the back end, is not one powerful computer but generally a network of commodity computers. Both the processing and storage of data is spread across multiple computers. In this blog we discuss how large datasets can be stored using multiple commodity computers. &lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;c1vct&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;4i6dr&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Partitioning is the process of breaking up a large dataset in parts so that each part can fit easily on the disk on one one node and be efficiently managed by each node. For very large data sets that cannot fit on 1 machine, data needs to be broken up into parts ( partition or shard). Each partition is stored on a different machine. This is just natural horizontal scaling.  But most important is that, when it time to read the partitioned data, we need to be able to find (efficiently) which partition and node has the data we want to read. &lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;64nb6&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;85hms&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Storage space is not the only benefit of partitioning. You are also spreading the compute required to read, write and process the data.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;dfi2g&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;d7l3s&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Partitioning is generally combined with replication to make the partitions highly available. But we do not discuss replication here. That is a topic for another blog.&lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiSe5txoTx7XDcRG55vV0ioCER-BV8v6zEJDNkdv985wda6m7zX9PX3S4WeOjG8-vuJq74sQs3xFLmJKEV3jYey-mTdciquqJSvdnR6pZB2we11ClsmvVUqqmhzG2esx835i-T5h2xVi_xRNCXvGoom4hhsJ36_iWpy8d083qCmRkcvRantl17WaYEAT4k/s960/PartioningFig1Fig2%20.png&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;720&quot; data-original-width=&quot;960&quot; height=&quot;370&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiSe5txoTx7XDcRG55vV0ioCER-BV8v6zEJDNkdv985wda6m7zX9PX3S4WeOjG8-vuJq74sQs3xFLmJKEV3jYey-mTdciquqJSvdnR6pZB2we11ClsmvVUqqmhzG2esx835i-T5h2xVi_xRNCXvGoom4hhsJ36_iWpy8d083qCmRkcvRantl17WaYEAT4k/w519-h370/PartioningFig1Fig2%20.png&quot; width=&quot;519&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;d7l3s&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;buh4a&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;fn540&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;image-component data-node-attrs=&quot;{&amp;quot;id&amp;quot;:&amp;quot;25o9n&amp;quot;,&amp;quot;containerData&amp;quot;:{&amp;quot;width&amp;quot;:{&amp;quot;size&amp;quot;:&amp;quot;CONTENT&amp;quot;},&amp;quot;alignment&amp;quot;:&amp;quot;CENTER&amp;quot;,&amp;quot;spoiler&amp;quot;:{&amp;quot;enabled&amp;quot;:false},&amp;quot;textWrap&amp;quot;:true},&amp;quot;image&amp;quot;:{&amp;quot;src&amp;quot;:{&amp;quot;id&amp;quot;:&amp;quot;eeeb94_ca10b041b2874aa4842d6eb04c90e2c9~mv2.png&amp;quot;},&amp;quot;width&amp;quot;:960,&amp;quot;height&amp;quot;:720},&amp;quot;link&amp;quot;:null,&amp;quot;disableExpand&amp;quot;:false,&amp;quot;altText&amp;quot;:&amp;quot;Partitioning diagram&amp;quot;,&amp;quot;caption&amp;quot;:null,&amp;quot;disableDownload&amp;quot;:false,&amp;quot;loading&amp;quot;:false,&amp;quot;loadingPercentage&amp;quot;:null,&amp;quot;tempData&amp;quot;:null}&quot;&gt;&lt;/image-component&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;7pp14&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;dnnq3&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;h2 class=&quot;hQtSd _51IVl&quot; dir=&quot;auto&quot; id=&quot;bo96e&quot; indentation=&quot;0&quot; level=&quot;2&quot; textstyle=&quot;[object Object]&quot;&gt;2.0 Types of partitioning&lt;/h2&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;1jr3e&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;There are 2 types of data that need to be considered: unstructured and structured.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;2is0l&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;6cpoi&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Most discussions on partitioning discuss partitioning of data in databases ( structured data ) but not unstructured data which is outside databases in plain files. This blog discusses both unstructured data and structured data. &lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;di2o8&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;h3 class=&quot;_07o0J _51IVl&quot; dir=&quot;auto&quot; id=&quot;7ho27&quot; indentation=&quot;0&quot; level=&quot;3&quot; textstyle=&quot;[object Object]&quot;&gt;2.1 Structured data&lt;/h3&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;dpvqu&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;8cqlf&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt; The problem is more interesting for databases because it is not enough to break up the dataset into smaller parts. During reads you need to be able to find the data. And you need to do it fast. When the database receives a query - &quot;Give me records for Customer X&quot;,  How does it know which node hosts the data ? Does the database have to send the request to all the nodes ? That would be quite inefficient. &lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;hbn4&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;92cko&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;The goal is thus to partition data and query it efficiently. Another goal is to ensure that distribution of data between partitions is even. You do not want a situation where partition 1 has 70% of the data and the other 5 partitions has the remaining 30%. This will overload partition 1 ( known as a hot spot) and you lose the benefits of partitioning.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;7pi2d&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;3i8hn&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;2 strategies are commonly used for database partitioning.  &lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;2ehdj&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;h4 class=&quot;yfJW- _51IVl&quot; dir=&quot;auto&quot; id=&quot;atd9f&quot; indentation=&quot;0&quot; level=&quot;4&quot; textstyle=&quot;[object Object]&quot;&gt;2.1.1 Range based partitioning&lt;/h4&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6ysetnnWPHoM4Z5_PhWiFpXJp4_9aKajf7vDu0pO-pqqrQmB2aK55dQ06k83GGX-Dtjn8R3c5hLk-mdtQXYSmXAH5qCmOHVSCN2lJU7LqtsTBP65JZNL529QN83RyrfjIumC-4UikQeUskhz5OAUW0ABw4lQ_5nz7Nmo6Zk55kNKJE8RljlkBxOYyGIs/s960/Range%20based%20partitioning.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;720&quot; data-original-width=&quot;960&quot; height=&quot;266&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6ysetnnWPHoM4Z5_PhWiFpXJp4_9aKajf7vDu0pO-pqqrQmB2aK55dQ06k83GGX-Dtjn8R3c5hLk-mdtQXYSmXAH5qCmOHVSCN2lJU7LqtsTBP65JZNL529QN83RyrfjIumC-4UikQeUskhz5OAUW0ABw4lQ_5nz7Nmo6Zk55kNKJE8RljlkBxOYyGIs/w456-h266/Range%20based%20partitioning.jpg&quot; width=&quot;456&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;2ejmn&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;2sqjp&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;image-component data-node-attrs=&quot;{&amp;quot;id&amp;quot;:&amp;quot;divot&amp;quot;,&amp;quot;containerData&amp;quot;:{&amp;quot;width&amp;quot;:{&amp;quot;custom&amp;quot;:&amp;quot;642&amp;quot;},&amp;quot;alignment&amp;quot;:&amp;quot;CENTER&amp;quot;,&amp;quot;spoiler&amp;quot;:{&amp;quot;enabled&amp;quot;:false},&amp;quot;textWrap&amp;quot;:true},&amp;quot;image&amp;quot;:{&amp;quot;src&amp;quot;:{&amp;quot;id&amp;quot;:&amp;quot;eeeb94_c3d02040738241798b95f6ed5a53d47a~mv2.jpg&amp;quot;},&amp;quot;width&amp;quot;:960,&amp;quot;height&amp;quot;:720},&amp;quot;link&amp;quot;:null,&amp;quot;disableExpand&amp;quot;:false,&amp;quot;altText&amp;quot;:&amp;quot;Range based partitioning&amp;quot;,&amp;quot;caption&amp;quot;:null,&amp;quot;disableDownload&amp;quot;:false,&amp;quot;loading&amp;quot;:false,&amp;quot;loadingPercentage&amp;quot;:null,&amp;quot;tempData&amp;quot;:null}&quot;&gt;&lt;/image-component&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;aqn91&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;1jau5&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;489je&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Database records are generally stored sorted based on the primary key.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;9dujb&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Initially there is one partition with zero records.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;a3s58&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;As clients write to the database, the size of the partition increases. When it reaches a certain size say 10MB or 64MB, it is split into two partitions.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;7fm02&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Each partition may be assigned to a different node.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;89hnk&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;This process is repeated as more data is added and partitions grow. If data is deleted and partitions shrink, then small partitions can be merged&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;9vn5f&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;cjba4&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;To efficiently query data, the database needs to do some book keeping&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;89l2m&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;	-- which key range is in which partition&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;6nma1&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;	-- which partition is at which node&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;e93k6&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;d2rn3&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Starting with 1 partition and 1 node is not efficient for obvious reasons and databases typically start with configured number of partitions or a number of partitions proportional to the number of nodes.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;2eusi&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;656cr&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;To balance the load on nodes, partitions may need to be moved between nodes.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;5255e&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;This is the strategy used by HBase, CockroachDb, MongoDb.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;76mau&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;h4 class=&quot;yfJW- _51IVl&quot; dir=&quot;auto&quot; id=&quot;1q3ep&quot; indentation=&quot;0&quot; level=&quot;4&quot; textstyle=&quot;[object Object]&quot;&gt;2.1.2 Hash based partitioning&lt;/h4&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgcRziWLAbdSiAXy3cy4Zxni5jUcvOnMJRGQk29-I6vpinUysYCIg-E9qt9vo7IMt7xyphROlSYnKhtCw2PM2B8Y10u3WyBU8wfZkoUuIuOgDoUv1QO43IgJV3Y8ztnzPrDulKeHGJhrA7jp-gxyAuIOXqkirceJkkpJ0q1lQJZkQTxZIyOZN0GdkBg3EA/s960/Hash%20based%20partitioning.jpg&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;720&quot; data-original-width=&quot;960&quot; height=&quot;354&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgcRziWLAbdSiAXy3cy4Zxni5jUcvOnMJRGQk29-I6vpinUysYCIg-E9qt9vo7IMt7xyphROlSYnKhtCw2PM2B8Y10u3WyBU8wfZkoUuIuOgDoUv1QO43IgJV3Y8ztnzPrDulKeHGJhrA7jp-gxyAuIOXqkirceJkkpJ0q1lQJZkQTxZIyOZN0GdkBg3EA/w404-h354/Hash%20based%20partitioning.jpg&quot; width=&quot;404&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;h4 class=&quot;yfJW- _51IVl&quot; dir=&quot;auto&quot; id=&quot;48urb&quot; indentation=&quot;0&quot; level=&quot;4&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/h4&gt;&lt;image-component data-node-attrs=&quot;{&amp;quot;id&amp;quot;:&amp;quot;2c50g&amp;quot;,&amp;quot;containerData&amp;quot;:{&amp;quot;width&amp;quot;:{&amp;quot;size&amp;quot;:&amp;quot;CONTENT&amp;quot;},&amp;quot;alignment&amp;quot;:&amp;quot;CENTER&amp;quot;,&amp;quot;spoiler&amp;quot;:{&amp;quot;enabled&amp;quot;:false},&amp;quot;textWrap&amp;quot;:true},&amp;quot;image&amp;quot;:{&amp;quot;src&amp;quot;:{&amp;quot;id&amp;quot;:&amp;quot;eeeb94_8e41aa45bbb0408db4ca243884e2275f~mv2.jpg&amp;quot;},&amp;quot;width&amp;quot;:960,&amp;quot;height&amp;quot;:720},&amp;quot;link&amp;quot;:null,&amp;quot;disableExpand&amp;quot;:false,&amp;quot;altText&amp;quot;:&amp;quot;hash based partitioning&amp;quot;,&amp;quot;caption&amp;quot;:null,&amp;quot;disableDownload&amp;quot;:false,&amp;quot;loading&amp;quot;:false,&amp;quot;loadingPercentage&amp;quot;:null,&amp;quot;tempData&amp;quot;:null}&quot;&gt;&lt;/image-component&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;fs3or&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;au1qg&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;fmsub&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;The hash value calculated from the key is used to determine the location where the record can be stored. &lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;fter1&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;6lfkd&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;The wrong way to determine the node is by using hash mod n, where n is the number of nodes. The problem with this approach is that when nodes are added or removed from the cluster, a very high percentage of the keys need to be removed.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;33m1p&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;9tt9a&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;A better approach is to start with a fixed number of partitions , way more that the number of the nodes the cluster will ever have , say  for example 1000 or 10000. Partitions are logical.  Hash ranges are assigned to partitions.  Partitions are assigned to nodes either using numPartitions mod numNodes  or other algorithms. This is shown in the top half of Figure 4. The bottom half of figure 4 visualizes the same as a ring as is done in many articles that to refers to this as consistent hashing. Think of partitions being placed on the ring. Each partition owns the key space from the position of the previous position to its position. The cluster needs to maintain a mapping of partitions to nodes. When a new node is added, the cluster can take a few partitions from existing nodes and assign to the new nodes. When a node is removed, the cluster assigns its partitions to other nodes. Looking up a key is a extra level of indirection. The hash of key maps to a partition. The partition node map tell you which node has the partition that has the key. Many studies has shown that this lead to less movement of keys between nodes as the cluster changes. &lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;1tl9b&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;8mstm&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;In popular press, this has the poorly understood name &quot;consistent hashing&quot;. It is just hast hash based partitioning. Nothing consistent and nothing to do with consistency. &lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;67r0d&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;h4 class=&quot;yfJW- _51IVl&quot; dir=&quot;auto&quot; id=&quot;2ekej&quot; indentation=&quot;0&quot; level=&quot;4&quot; textstyle=&quot;[object Object]&quot;&gt;2.1.3 Secondary Indexes&lt;/h4&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;6qb3u&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;3ibfk&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;So far we have been talking about partitioning by the primary key, also know as the primary index.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;9r231&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;aeei0&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;To speed up retrieval of records, databases also have secondary indexes which can be very large  and might need to be partitioned.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;dhkrm&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;fltng&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;One approach can be to keep the secondary indexes local to the node on which the primary index partition is. The advantage of this approach is that since all related rows are on the same node, inserts/updates/deletes are are all local. But queries on the secondary index requires sending queries to all nodes and aggregating the responses.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;9jb80&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;64p61&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Another approach is to create a global secondary index and partition it as an independent entity.  However since the secondary index partitions might be on a different partition from the primary partition, CUD operations are more expensive. Transactions might be distributed. However range queries on secondary indexes are more efficient since closer records (by sort) are on the same partition.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;dr792&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt; &lt;/p&gt;&lt;h3 class=&quot;_07o0J _51IVl&quot; dir=&quot;auto&quot; id=&quot;77f5s&quot; indentation=&quot;0&quot; level=&quot;3&quot; textstyle=&quot;[object Object]&quot;&gt;2.2 Unstructured data&lt;/h3&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;efnto&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;9euqd&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Unstructured data refer to ordinary files that have text or binary data. Of course we are talking about large files or many large files. This is the use case for a distributed file system such as HDFS (hadoop file system) or GFS ( Amazon ). Logically the implementation of distributed file system is similar to say a linux filesystem. You view the file system as a list of blocks of fixed size. On a single node linux file system , all the disk blocks are on one node. In a distributed file system, the blocks are spread across multiple nodes. In HDFS, the name node maintains the metadata for the distributed filesystem -- given a file , which blocks make up the file and which nodes have the blocks.  To create a file, the name node may assign a block on a particular node and the client talks directly to service called data node running on the target node to write to block. To read a block, the name node directs the client to the data node that hosts the block and the client reads directly from that block. But the basic algorithm is simple -- break up the file data into blocks and spread them across nodes. &lt;/p&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEih45b6o0Tphddi_LVyf9N1zqfK-yl8L5VzL9s9EButbtPVDk5lPkd1zKIg7zIlubpxbO10jyjWPvcAss4qMTKnYdlwvGOXmV9W8tnXW0zUIGATPUxCr0g65RVyuBSX9CQ50DEBgPBycwAV3CF-3iJCCEszH3Q2eylK4xpGSHEoOgEXTSwuAkp7KYGf1u4/s960/Distributed%20file%20system.png&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;720&quot; data-original-width=&quot;960&quot; height=&quot;345&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEih45b6o0Tphddi_LVyf9N1zqfK-yl8L5VzL9s9EButbtPVDk5lPkd1zKIg7zIlubpxbO10jyjWPvcAss4qMTKnYdlwvGOXmV9W8tnXW0zUIGATPUxCr0g65RVyuBSX9CQ50DEBgPBycwAV3CF-3iJCCEszH3Q2eylK4xpGSHEoOgEXTSwuAkp7KYGf1u4/w457-h345/Distributed%20file%20system.png&quot; width=&quot;457&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;9euqd&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;9c3g&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;8qvds&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;image-component data-node-attrs=&quot;{&amp;quot;id&amp;quot;:&amp;quot;52mrf&amp;quot;,&amp;quot;containerData&amp;quot;:{&amp;quot;width&amp;quot;:{&amp;quot;size&amp;quot;:&amp;quot;CONTENT&amp;quot;},&amp;quot;alignment&amp;quot;:&amp;quot;CENTER&amp;quot;,&amp;quot;spoiler&amp;quot;:{&amp;quot;enabled&amp;quot;:false},&amp;quot;textWrap&amp;quot;:true},&amp;quot;image&amp;quot;:{&amp;quot;src&amp;quot;:{&amp;quot;id&amp;quot;:&amp;quot;eeeb94_b0d304adb8144120a61ce903a4ddd0ea~mv2.png&amp;quot;},&amp;quot;width&amp;quot;:960,&amp;quot;height&amp;quot;:720},&amp;quot;link&amp;quot;:null,&amp;quot;disableExpand&amp;quot;:false,&amp;quot;altText&amp;quot;:&amp;quot;distributed file system HDFS&amp;quot;,&amp;quot;caption&amp;quot;:null,&amp;quot;disableDownload&amp;quot;:false,&amp;quot;loading&amp;quot;:false,&amp;quot;loadingPercentage&amp;quot;:null,&amp;quot;tempData&amp;quot;:null}&quot;&gt;&lt;/image-component&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;c4h1m&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;4a87o&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Another example is partitioning of topic logs in Apache Kafka is a messaging system ( they like to call it event streaming) where producers write messages to a topic and consumers read messages from the topic. The storage for a topic is a log file. New messages are appended to the end of log file. They are read from the front. Obviously the logs can grow beyond what can fit on a node. So log is broken into partitions and distributed across multiple nodes. The broker serves producers and consumer. A Kafka cluster has multiple brokers with each broker running on a seprate node managing multiple partitions.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;32djh&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;h2 class=&quot;hQtSd _51IVl&quot; dir=&quot;auto&quot; id=&quot;c805k&quot; indentation=&quot;0&quot; level=&quot;2&quot; textstyle=&quot;[object Object]&quot;&gt;3.0 Rebalancing&lt;/h2&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;bsa5i&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;c0mt6&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Rebalancing is the process of moving partitions between nodes to make the distribution of load even across all the nodes.  This is necessary when nodes join or leave the cluster or if the cluster starts receiving more data for certain keys. Either way rebalancing is an expensive operation that needs lot of CPU, memory and network bandwidth. It can have an impact on the performance of regular CRUD processing.  In a ideal world, we would like rebalancing to happen automatically behind the scenes, without end users knowing about it. But for performance reasons listed above, that rarely works well in practice. Some databases require an admin to manually start a rebalance, which can be done during a period of low load and monitored.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;dmp63&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;h2 class=&quot;hQtSd _51IVl&quot; dir=&quot;auto&quot; id=&quot;4ctvs&quot; indentation=&quot;0&quot; level=&quot;2&quot; textstyle=&quot;[object Object]&quot;&gt;4.0 Routing&lt;/h2&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;abg0k&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;How does a client know which partition to connect to ? The short answer is that the database has to maintain mapping of partition to node. In the case of hash based partitioning, hash maps to a partition which maps to a node. In the case of range based partitioning the key  maps to a key range which maps to a partition which maps to a node. The partition node map is available to nodes. If a client can connect to any node, then if the node does not have the partition to handle the clients request, it can redirect the client to the appropriate node or it can get the data from the target node and return to client.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;6ntcu&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;h2 class=&quot;hQtSd _51IVl&quot; dir=&quot;auto&quot; id=&quot;7oeqv&quot; indentation=&quot;0&quot; level=&quot;2&quot; textstyle=&quot;[object Object]&quot;&gt;5.0 Use case&lt;/h2&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;7kj83&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;9u301&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;There are cases where you might not have a ready made database doing server side partitioning for you and you might need to do it yourself. Or even when the database does it for you, you still need to pick the right partition key for the partitioning to be optimal. Let us look at some large datasets and discuss how they might be logically partitioned.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;3qmlh&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;a4ohq&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Let us say you are building a twitter like system. &lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;e49v0&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;74a4n&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Say 100 million tweets of 140 character per day&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;42asb&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;100M * ( 280 bytes + 20 bytes for id, timestamp)&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;rlg6&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;30 GB / day&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;5e4p0&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;10 TB / year&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;2s9v2&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Need to store 5 years to data&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;emmlh&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Need to store and query 50 TB  &lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;58hnp&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;bs4bj&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;How does twitter work ?&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;ep9i2&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Users follow other users.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;but98&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;When a user connects, we need to show the most recent tweets from the users he follows.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;39hda&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;60h63&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;So we need to store about 50 TB of tweets. Given a user, we need to query say the 50 most recent tweets from the users he follows.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;41ork&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;aem17&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Using commodity hardware, 50TB would need say 25 nodes. What key would you use to partition the data ?&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;4up97&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;ea1qv&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Option 1 :  hash based partitioning based on user.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;28bft&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;To store tweets, a hash of the user is to used to locate the node where the tweet is stored.  To query - for each user that the user follows,  use the hash to query the node for that users tweets. A problem with this approach is that some users tweet way more than other users. Their nodes are going to be overloaded while others are idle. Unbalanced load.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;di0ti&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;d6sir&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Option 2 : hash based on randomly generated tweet id&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;8vm75&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;The problem with this approach is the for every query, you have to query every server and aggregate the results. Inefficient for queries.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;8nhit&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;5n378&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Option 3: hash based on timestamp&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;8jib2&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Timestamp is relevant because for each feed request we want the latest tweets. It would be good if tweets are sorted by timestamp. However with such as hash, at any given point in time , one server is overloaded as all the writes are going to the server.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;6vpdc&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;2jo96&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Option 4:&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;7gohg&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Given the choices, inefficient query (option 2) is more tolerable that unbalanced load (option 1) which could crash some of the servers making the system unavailable. But we also want queries to return the most recent data (sorted by timestamp). So we can improve querying little bit by combining option 2 and 3. Assume a timestamp in epoch time in increments of 1 sec, the tweetid could be timestamp + auto incremented seq. The sequence gives randomness to the tweet id and will give uniform distribution across node.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;4bm19&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;So given a epoch 1692547708 you will have tweet ids like&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;db846&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;1692547708 1&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;43nel&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;1692547708 2&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;81i62&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;1692547708 3&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;8bhr1&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;au5uj&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;co109&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;1692547708 n&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;7199f&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;ceckg&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Of course you are wondering how to generate unique tweet ids in a distributed system. That is a topic for another blog.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;5uvfm&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;h2 class=&quot;hQtSd _51IVl&quot; dir=&quot;auto&quot; id=&quot;a2v9i&quot; indentation=&quot;0&quot; level=&quot;2&quot; textstyle=&quot;[object Object]&quot;&gt;6.0 Summary &lt;/h2&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;dpupk&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;vep2&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Partitioning data and spreading it across nodes is fundamental to distributed system. Special thought needs to be given to how the partitioned data can be queried efficiently. Hash based partitioning and range based partitioning are two popular strategies. Nodes can fail or additional nodes may need to added to scale. To ensure that load is even across nodes, partitions may be moved between nodes in a process called rebalancing. For best results design your partition keys so that load is distributed evenly and querying is efficient.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;as7q5&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;h2 class=&quot;hQtSd _51IVl&quot; dir=&quot;auto&quot; id=&quot;8cmqs&quot; indentation=&quot;0&quot; level=&quot;2&quot; textstyle=&quot;[object Object]&quot;&gt;7.0 Related Content&lt;/h2&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;1i39k&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;a class=&quot;QPmB- brcvo&quot; data-hook=&quot;WebLink&quot; href=&quot;https://khangaonkar.blogspot.com/2024/06/cockroachdb-review-should-i-use.html&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;&lt;u&gt;CockroachDB Review&lt;/u&gt;&lt;/a&gt; &lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;35od7&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;7mhhe&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;ac6k7&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;djqhv&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;drv6&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;8cb4a&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;4j5d9&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;cdktb&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;ejj1h&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;5909m&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;e0i6f&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;29j65&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;1imdu&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;usbv&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;31np1&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;fsgp3&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;7t6d3&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;dccsi&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/7271596687817394343'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/7271596687817394343'/><link rel='alternate' type='text/html' href='http://khangaonkar.blogspot.com/2024/07/how-modern-distributed-systems-scale-by.html' title='How modern distributed systems scale by partitioning ?'/><author><name>Manoj Khangaonkar</name><uri>http://www.blogger.com/profile/13925926828045222969</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiSe5txoTx7XDcRG55vV0ioCER-BV8v6zEJDNkdv985wda6m7zX9PX3S4WeOjG8-vuJq74sQs3xFLmJKEV3jYey-mTdciquqJSvdnR6pZB2we11ClsmvVUqqmhzG2esx835i-T5h2xVi_xRNCXvGoom4hhsJ36_iWpy8d083qCmRkcvRantl17WaYEAT4k/s72-w519-h370-c/PartioningFig1Fig2%20.png" height="72" width="72"/></entry><entry><id>tag:blogger.com,1999:blog-5008017311510568944.post-6818067843950738370</id><published>2024-06-30T16:26:00.000-07:00</published><updated>2024-06-30T16:26:19.910-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="architecture"/><category scheme="http://www.blogger.com/atom/ns#" term="BigData"/><category scheme="http://www.blogger.com/atom/ns#" term="CockRoachDB"/><category scheme="http://www.blogger.com/atom/ns#" term="Databases"/><category scheme="http://www.blogger.com/atom/ns#" term="distributed applications"/><category scheme="http://www.blogger.com/atom/ns#" term="Distributed systems"/><title type='text'>CockroachDb Review: Should I use CockroachDb ?</title><content type='html'>&lt;h1 style=&quot;text-align: left;&quot;&gt;&amp;nbsp;Overview&lt;/h1&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;cebub&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;CockroachDb is a modern distributed database that promises linear scalability with strict serializability. &lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;5hinh&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;8oupa&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt; Server side sharding is automatic. Nodes can be added easily as needed. Claims to provide the SERIALIZABLE isolation level.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;6lrua&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;5h05d&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Most distributed databases such as Cassandra, MongoDb, HBase etc sacrifice consistency to achieve high availability.  CockroachDb distinguishes itself by claiming to be distributed and the same time offer strong consistency that even single node databases do not offer.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;b79bq&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;fp4tq&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;This falls into a database category called NewSql or DistributedSQL as opposed to NoSql (Cassandra, MongoDb)&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;dau8e&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;h2 class=&quot;hQtSd _51IVl&quot; dir=&quot;auto&quot; id=&quot;84vjq&quot; indentation=&quot;0&quot; level=&quot;2&quot; textstyle=&quot;[object Object]&quot;&gt;When to choose CockroachDb ?&lt;/h2&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;1fv8i&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;You should choose CockroachDb if&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;1fv8i&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;Your data is of a global scale.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;1fv8i&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;span&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;As data size increases, you need to scale horizontally to several nodes.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;77kth&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;	&lt;span&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;You need data to be distributed and localized in specific geographical regions. For &lt;br id=&quot;&quot; /&gt;	&lt;span&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;example EU data resides in Europe while US data resides in US.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;ajh52&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;	&lt;span&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;You need strong consistency. Serializable isolation level.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;29d9a&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;	&lt;span&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;You need to keep the SQL / relational data model.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;47i5j&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;	&lt;span&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;You need distributed transactions.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;a3ri2&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;1hdde&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;You may want to pass on it if&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;6gjhn&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;	&lt;span&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;You data size can easily fit on a node for the foreseeable future.&lt;br id=&quot;&quot; /&gt;	&lt;span&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;You organization is more comfortable with a stable proven database. (CockroachDb is &lt;br id=&quot;&quot; /&gt;	&lt;span&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;still maturing).&lt;br id=&quot;&quot; /&gt;	&lt;span&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;You data model is heavily normalized and you do a lot of joins in your queries. While this &lt;br id=&quot;&quot; /&gt;	&lt;span&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;database can support joins, it is still not recommended in a highly distributed &lt;br id=&quot;&quot; /&gt;	&lt;span&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;environment.&lt;/p&gt;&lt;h2 class=&quot;hQtSd _51IVl&quot; dir=&quot;auto&quot; id=&quot;88b4c&quot; indentation=&quot;0&quot; level=&quot;2&quot; textstyle=&quot;[object Object]&quot;&gt;Architecture&lt;/h2&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;c15dq&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Architecture is based on &lt;a class=&quot;QPmB- brcvo&quot; data-hook=&quot;WebLink&quot; href=&quot;https://static.googleusercontent.com/media/research.google.com/en//archive/spanner-osdi2012.pdf&quot; rel=&quot; noopener&quot; target=&quot;_blank&quot;&gt;Google&#39;s Spanner paper&lt;/a&gt;.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;fll7i&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;It is a key value store with a SQL interface on top of it.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;cd5q3&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Database is a cluster of nodes. All nodes are equal. Nodes may join and leave the cluster at any time. &lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;242sv&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Sorted map of key values pairs. Fully ordered monolithic key space. All tables/indexes go into the same key space by encoding tablename/indexname/key together.&lt;/p&gt;&lt;h3 class=&quot;_07o0J _51IVl&quot; dir=&quot;auto&quot; id=&quot;18s3p&quot; indentation=&quot;0&quot; level=&quot;3&quot; textstyle=&quot;[object Object]&quot;&gt;Sharding&lt;/h3&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;16ah9&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Key value pairs are broken up into contiguous ranges.&amp;nbsp;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;16ah9&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;When range size reaches 512 Mib (2 power 20) It is split into 2 ranges.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;16ah9&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Each range is assigned to a node and replicated.&amp;nbsp;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;16ah9&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;If you have 1 node all the shards are in that node. To scale, you add more nodes and the shards get distributed across nodes. A minimum of 3 nodes is recommended.&amp;nbsp;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;16ah9&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Very easily spin up node(s) and add to cluster anywhere.&amp;nbsp;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;16ah9&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Btree like index structure used to locate shard that has a key.&lt;/p&gt;&lt;h3 class=&quot;_07o0J _51IVl&quot; dir=&quot;auto&quot; id=&quot;45r3k&quot; indentation=&quot;0&quot; level=&quot;3&quot; textstyle=&quot;[object Object]&quot;&gt;Replication&lt;/h3&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;6vqso&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Data in each range is replicated using the Raft consensus algorithm.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;9q40b&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;A minimum replication factor of 3 is needed.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;1uebj&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;This provides the high availability. Data is available as long as a majority of the nodes in the cluster are available.&lt;/p&gt;&lt;h3 class=&quot;_07o0J _51IVl&quot; dir=&quot;auto&quot; id=&quot;6aqdo&quot; indentation=&quot;0&quot; level=&quot;3&quot; textstyle=&quot;[object Object]&quot;&gt;Geo-partitioning&lt;/h3&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;93h0q&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;By adding a country or region to the primary key, you can limit storage to keys to a particular region. So European data can be make to reside in Europe, US data in US and so. This has 2 benefits&lt;br id=&quot;&quot; /&gt;	There is a performance benefit and data is local to its users.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;93h0q&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;	It can satisfy legal requirements where data is not allowed to leave a country or region. &lt;/p&gt;&lt;h3 class=&quot;_07o0J _51IVl&quot; dir=&quot;auto&quot; id=&quot;iite&quot; indentation=&quot;0&quot; level=&quot;3&quot; textstyle=&quot;[object Object]&quot;&gt;Read/Write&lt;/h3&gt;&lt;h4 class=&quot;yfJW- _51IVl&quot; dir=&quot;auto&quot; id=&quot;dal0l&quot; indentation=&quot;0&quot; level=&quot;4&quot; textstyle=&quot;[object Object]&quot;&gt;Reads&lt;/h4&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;4sdv8&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Any node can receive a request to read a key/value.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;4sdv8&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Request is forwarded to the node that is the raft leader for that table/range.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;4sdv8&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Leader returns the data to the node that requested it. Since leader returns the data, no consensus is required.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;4sdv8&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Node returns it to the client.&lt;/p&gt;&lt;h4 class=&quot;yfJW- _51IVl&quot; dir=&quot;auto&quot; id=&quot;496nr&quot; indentation=&quot;0&quot; level=&quot;4&quot; textstyle=&quot;[object Object]&quot;&gt;Writes&lt;/h4&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;bbouh&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Any node can receive a request to write a key/value.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;a6t9t&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Request is forwarded to the node that is the raft leader for that table/range.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;a6t9t&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Leader writes the value to its log and initiates consensus with replicas for the range. When majority acknowledges, the key/value is considered committed and leader notifies the requesting node which notifies the client &lt;/p&gt;&lt;h3 class=&quot;_07o0J _51IVl&quot; dir=&quot;auto&quot; id=&quot;9rvoo&quot; indentation=&quot;0&quot; level=&quot;3&quot; textstyle=&quot;[object Object]&quot;&gt;Transactions&lt;/h3&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;ajkf7&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Supports transactions that spans multiple tables and rows.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;ajkf7&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Transactions can be distributed, that is span multiple nodes.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;ajkf7&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;The supported isolation level is strict serializability which is the highest isolation level. Strict serializability means that not only are transactions ordered, but they are ordered as per wall clock time.&lt;br id=&quot;&quot; /&gt;Transaction protocol is an improvement over two phase commit. In parallel, participants acquire locks and create write intents. The transaction is marked staged. When the client commits, if all locks are acquired and writes are replicated, the coordinator immediately returns success to client. In background the transaction is marked committed. This is one round trip between transaction coordinator and each participant - unlike two phase commit - which requires two round trips.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;ajkf7&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Hybrid logical clocks are used to timestamp each transaction. Timestamp is the version for MVCC.&lt;/p&gt;&lt;h2 class=&quot;hQtSd _51IVl&quot; dir=&quot;auto&quot; id=&quot;ere6s&quot; indentation=&quot;0&quot; level=&quot;2&quot; textstyle=&quot;[object Object]&quot;&gt;Data Model&lt;/h2&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;aa3qo&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Clients see only the SQL row column relation model&lt;br id=&quot;&quot; /&gt;Wire protocol is same as Postgresql wire protocol.&lt;/p&gt;&lt;h2 class=&quot;hQtSd _51IVl&quot; dir=&quot;auto&quot; id=&quot;7l415&quot; indentation=&quot;0&quot; level=&quot;2&quot; textstyle=&quot;[object Object]&quot;&gt;Performance&lt;/h2&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;crq9d&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt; Efficient range scan.&lt;br id=&quot;&quot; /&gt;Geo partitioning improves performance by locality.&lt;br id=&quot;&quot; /&gt;Distributed SQL execution.&lt;br id=&quot;&quot; /&gt;Distributed transactions will be slow.&lt;br id=&quot;&quot; /&gt;Generally you do not want distributed transactions over large distances. If you build a 3 node CockroachDb cluster with 1 node in NewYork, 1 in London and 1 in San Francisco, the write latencies are going to be very high due to the round trips for RAFT and distributed transactions. The cluster topology needs to be designed appropriately to give you the lowest latency at the desired level of high availability.&lt;/p&gt;&lt;h2 class=&quot;hQtSd _51IVl&quot; dir=&quot;auto&quot; id=&quot;1ttae&quot; indentation=&quot;0&quot; level=&quot;2&quot; textstyle=&quot;[object Object]&quot;&gt;Administration&lt;/h2&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;6dc35&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Good command line tools and UI console make the the administration easy.&lt;br id=&quot;&quot; /&gt;Since all nodes are equals, number of moving parts that need to be administered is low.&lt;/p&gt;&lt;h2 class=&quot;hQtSd _51IVl&quot; dir=&quot;auto&quot; id=&quot;4c0p1&quot; indentation=&quot;0&quot; level=&quot;2&quot; textstyle=&quot;[object Object]&quot;&gt;Summary&lt;/h2&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;d3o98&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;If you need a globally distributed database with strict serializability, this is definitely a database to look at. It has good pedigree. However remember that distributed databases are not drop in replacement for your traditional RDBMSs. Distributed queries especially joins and distributed transaction can be slow. So some application redesign, some denormalization is always required.&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;d3o98&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;Note: Moved from heavydutysoftware.com&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;d354u&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;p class=&quot;g9B03 BIAsr&quot; id=&quot;9u9is&quot; indentation=&quot;0&quot; textstyle=&quot;[object Object]&quot;&gt;&lt;/p&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/6818067843950738370'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/6818067843950738370'/><link rel='alternate' type='text/html' href='http://khangaonkar.blogspot.com/2024/06/cockroachdb-review-should-i-use.html' title='CockroachDb Review: Should I use CockroachDb ?'/><author><name>Manoj Khangaonkar</name><uri>http://www.blogger.com/profile/13925926828045222969</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-5008017311510568944.post-8300504441663395606</id><published>2022-02-28T16:04:00.000-08:00</published><updated>2024-07-21T09:13:22.256-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Database MySql Distributed Scale HighAvailability Clustering"/><title type='text'>Quick Review: Mysql NDB cluster</title><content type='html'>&lt;p&gt;&amp;nbsp;This is a quick 2 min overview of Mysql NDB Cluster. The goal is to help you decide within a minute or two, whether this is an appropriate solution for you.&lt;/p&gt;&lt;p&gt;Cluster of in-memory Mysql databases with a shared nothing architecture.&lt;/p&gt;&lt;p&gt;Consists of Mysql nodes and data nodes.&lt;/p&gt;&lt;p&gt;Mysql nodes are Mysql servers that get data from data nodes. Data nodes hold the data using the NDB storage engine. There are also admin nodes.&lt;/p&gt;&lt;p&gt;NDB nodes serve the data from memory. Data is persisted at checkpoints.&lt;/p&gt;&lt;p&gt;Data is partitioned and replicated.&lt;/p&gt;&lt;p&gt;Up to 48 data nodes and 2 replicas for each fragment of data.&lt;/p&gt;&lt;p&gt;ACID compliant.&lt;/p&gt;&lt;p&gt;READ_COMMITTED&amp;nbsp; isolation level.&lt;/p&gt;&lt;p&gt;Sharding of data is done automatically. No involvement of user or application is required.&lt;/p&gt;&lt;p&gt;Data is replicated for high availability. Node failures are handled automatically.&lt;/p&gt;&lt;p&gt;Clients can access data using NDB Api. Both SQL and NOSQL styles are possible.&lt;/p&gt;&lt;p&gt;This is not a good general purpose database. It is suitable for certain specific use cases of telecom and game but not for general OLTP.&lt;/p&gt;&lt;p&gt;Feels like it has too many moving parts to manage.&lt;/p&gt;&lt;p&gt;High performance -- it is serving data from memory.&lt;/p&gt;&lt;p&gt;&lt;b&gt;Summary&lt;/b&gt;&lt;/p&gt;&lt;p&gt;Not a general purpose distributed databases. Unless you are in telecom or gaming or know for sure why this meets your use case, don&#39;nt even think about it.&lt;/p&gt;&lt;p&gt;If you are on Mysql and want high availability, try &lt;a href=&quot;https://khangaonkar.blogspot.com/2022/02/quick-review-mysql-innodb-cluster.html&quot;&gt;Mysql InnoDb Cluster&lt;/a&gt;, which is much easier to understand and use.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/8300504441663395606'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/8300504441663395606'/><link rel='alternate' type='text/html' href='http://khangaonkar.blogspot.com/2022/02/quick-review-mysql-ndb-cluster.html' title='Quick Review: Mysql NDB cluster'/><author><name>Manoj Khangaonkar</name><uri>http://www.blogger.com/profile/13925926828045222969</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-5008017311510568944.post-7373732140707562437</id><published>2022-02-14T16:27:00.001-08:00</published><updated>2022-02-14T16:31:59.198-08:00</updated><title type='text'>Quick Review: Mysql InnoDb Cluster</title><content type='html'>&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;This is a quick 2 min overview of Mysql InnoDb Cluster. The goal is to help you decide within a minute or two, whether this is an appropriate solution for you.&lt;/p&gt;&lt;p&gt;Simple HA solution for Mysql.&lt;/p&gt;&lt;p&gt;Built on top of MySql group replication.&lt;/p&gt;&lt;p&gt;It has 3 Components:&lt;/p&gt;&lt;p&gt;Replication: Uses existing mysql asynchronous replication capabilities. Default is Primary and secondary configuration. Writes go to master which replicates to slaves. Slaves can service reads&lt;/p&gt;&lt;p&gt;Mysql router: Provides routing between your application and the cluster. Supports automatic failover. If the primary dies. The router will redirect writes to the secondary that takes over.&lt;/p&gt;&lt;p&gt;Mysql shell: This is an advance shell that let you code and configure the cluster.&lt;/p&gt;&lt;p&gt;Works best over a local area network. Performance degrades over wide area networks&lt;/p&gt;&lt;p&gt;Easy to setup. Simple commands that are entered on the mysql shell.&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;span class=&quot;token keyword&quot; style=&quot;background: rgb(248, 248, 248); border: 0px; box-sizing: inherit; color: #0077aa; font-family: &amp;quot;Liberation Mono&amp;quot;, Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 12.8304px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre;&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;background-color: #f8f8f8; font-family: &amp;quot;Liberation Mono&amp;quot;, Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 12.8304px; white-space: pre;&quot;&gt; cluster &lt;/span&gt;&lt;span class=&quot;token operator&quot; style=&quot;background: rgb(248, 248, 248); border: 0px; box-sizing: inherit; color: #a67f59; font-family: &amp;quot;Liberation Mono&amp;quot;, Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 12.8304px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;background-color: #f8f8f8; font-family: &amp;quot;Liberation Mono&amp;quot;, Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 12.8304px; white-space: pre;&quot;&gt; dba&lt;/span&gt;&lt;span class=&quot;token punctuation&quot; style=&quot;background: rgb(248, 248, 248); border: 0px; box-sizing: inherit; color: #999999; font-family: &amp;quot;Liberation Mono&amp;quot;, Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 12.8304px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre;&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot; style=&quot;background: rgb(248, 248, 248); border: 0px; box-sizing: inherit; color: #dd4a68; font-family: &amp;quot;Liberation Mono&amp;quot;, Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 12.8304px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre;&quot;&gt;createCluster&lt;/span&gt;&lt;span class=&quot;token punctuation&quot; style=&quot;background: rgb(248, 248, 248); border: 0px; box-sizing: inherit; color: #999999; font-family: &amp;quot;Liberation Mono&amp;quot;, Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 12.8304px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot; style=&quot;background: rgb(248, 248, 248); border: 0px; box-sizing: inherit; color: #669900; font-family: &amp;quot;Liberation Mono&amp;quot;, Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 12.8304px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre;&quot;&gt;&#39;&lt;em class=&quot;replaceable&quot; style=&quot;background: transparent; border: 0px; box-sizing: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;&quot;&gt;testCluster&lt;/em&gt;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot; style=&quot;background: rgb(248, 248, 248); border: 0px; box-sizing: inherit; color: #999999; font-family: &amp;quot;Liberation Mono&amp;quot;, Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 12.8304px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre;&quot;&gt;)&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot;color: black; font-family: &amp;quot;Liberation Mono&amp;quot;, Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 12.8304px; white-space: pre;&quot;&gt;cluster&lt;/span&gt;&lt;span class=&quot;token punctuation&quot; style=&quot;background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; box-sizing: inherit; color: #999999; font-family: &amp;quot;Liberation Mono&amp;quot;, Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 12.8304px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre;&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot; style=&quot;background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; box-sizing: inherit; color: #dd4a68; font-family: &amp;quot;Liberation Mono&amp;quot;, Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 12.8304px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre;&quot;&gt;addInstance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot; style=&quot;background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; box-sizing: inherit; color: #999999; font-family: &amp;quot;Liberation Mono&amp;quot;, Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 12.8304px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot; style=&quot;background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; box-sizing: inherit; color: #669900; font-family: &amp;quot;Liberation Mono&amp;quot;, Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 12.8304px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre;&quot;&gt;&#39;server1@host1:3306&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot; style=&quot;background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; box-sizing: inherit; color: #999999; font-family: &amp;quot;Liberation Mono&amp;quot;, Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 12.8304px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre;&quot;&gt;)&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span class=&quot;token punctuation&quot; style=&quot;background: rgb(248, 248, 248); border: 0px; box-sizing: inherit; color: #999999; font-family: &amp;quot;Liberation Mono&amp;quot;, Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 12.8304px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre;&quot;&gt;&lt;span class=&quot;token punctuation&quot; style=&quot;background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; box-sizing: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;&quot;&gt;&lt;span style=&quot;color: black;&quot;&gt;cluster&lt;/span&gt;&lt;span class=&quot;token punctuation&quot; style=&quot;background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; box-sizing: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot; style=&quot;background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; box-sizing: inherit; color: #dd4a68; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;&quot;&gt;addInstance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot; style=&quot;background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; box-sizing: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot; style=&quot;background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; box-sizing: inherit; color: #669900; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;&quot;&gt;&#39;server2@host2:3306&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot; style=&quot;background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; box-sizing: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span class=&quot;token punctuation&quot; style=&quot;background: rgb(248, 248, 248); border: 0px; box-sizing: inherit; color: #999999; font-family: &amp;quot;Liberation Mono&amp;quot;, Consolas, Monaco, &amp;quot;Andale Mono&amp;quot;, &amp;quot;Ubuntu Mono&amp;quot;, monospace; font-size: 12.8304px; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline; white-space: pre;&quot;&gt;&lt;span class=&quot;token punctuation&quot; style=&quot;background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; box-sizing: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;&quot;&gt;&lt;span class=&quot;token punctuation&quot; style=&quot;background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; box-sizing: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;&quot;&gt;&lt;span style=&quot;color: black;&quot;&gt;cluster&lt;/span&gt;&lt;span class=&quot;token punctuation&quot; style=&quot;background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; box-sizing: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot; style=&quot;background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; box-sizing: inherit; color: #dd4a68; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;&quot;&gt;addInstance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot; style=&quot;background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; box-sizing: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot; style=&quot;background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; box-sizing: inherit; color: #669900; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;&quot;&gt;&#39;server3@host3:3306&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot; style=&quot;background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: 0px; box-sizing: inherit; margin: 0px; outline: 0px; padding: 0px; vertical-align: baseline;&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Cluster elects the primary. If you want a particular server to be the primary, you can give it extra weight.&lt;/p&gt;&lt;p&gt;Client do not connect directly to the servers. Rather they connect to the Mysql router that provides the routing as well failover.&lt;/p&gt;&lt;p&gt;MySql InnoDB clusterSet provide additional resiliency by replicating data from a primary cluster to a cluster in another datacenter or location. If the primary cluster becomes available, one of the secondary cluster can become the primary.&lt;/p&gt;&lt;p&gt;&lt;b&gt;Summary&lt;/b&gt;&lt;/p&gt;&lt;p&gt;Provides scalability for reads and some HA for Mysql deployments. Simple, easy to use solution. No sharding. Some consistency issues will there when you read from replicas that lag a little bit&lt;/p&gt;&lt;p&gt;References:&lt;/p&gt;&lt;p&gt;https://dev.mysql.com/doc/mysql-shell/8.0/en/mysql-innodb-cluster.html&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/7373732140707562437'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/7373732140707562437'/><link rel='alternate' type='text/html' href='http://khangaonkar.blogspot.com/2022/02/quick-review-mysql-innodb-cluster.html' title='Quick Review: Mysql InnoDb Cluster'/><author><name>Manoj Khangaonkar</name><uri>http://www.blogger.com/profile/13925926828045222969</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-5008017311510568944.post-8285583592047900270</id><published>2020-11-01T06:26:00.000-08:00</published><updated>2020-11-01T06:26:03.120-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="architecture"/><category scheme="http://www.blogger.com/atom/ns#" term="Databases"/><category scheme="http://www.blogger.com/atom/ns#" term="distributed applications"/><category scheme="http://www.blogger.com/atom/ns#" term="Distributed systems"/><category scheme="http://www.blogger.com/atom/ns#" term="NewSql"/><title type='text'>Building Globally Distributed Applications</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;&lt;p class=&quot;graf graf--p&quot; name=&quot;5e71&quot;&gt;A globally distributed application is one where the services and data for the application are partitioned and replicated across multiple regions over the globe. Popular distributed applications that everyone is familiar with are Facebook, Amazon.com, Gmail, Twitter, Instagram. However more and more enterprise applications are finding the need to become distributed because their user base is increasingly distributed around the globe. But not every company has the expertise of a Facebook or Amazon or Google. When going distributed, it is not enough to just spin up instances of your service on AWS or Google cloud on various regions. There are issues related to data that must be addressed for the application to work correctly. While consumer centric social media applications can tolerate some correctness issues or lags in data, the same might not be true for enterprise applications. This blog discusses the data and database issues related to a globally distributed application. Lastly, we discuss 2 research papers that been around since early part of this decade, but whose relevance is increasing in recent times.&lt;/p&gt;&lt;/div&gt;&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;Building globally distributed applications that are scalable, highly available and consistent can be challenging. Sharding has to be managed by the application. Keep it highly available requires non database tools. When you have been on a single node database whether it is Mysql or Postgresql etc, it is tempting to scale by manual sharding or one of the clustering solutions available for those databases. It might appear easy at the beginning but the cost of managing the system increases exponentially with scale. Additionally, sharding and replication lead to consistency issues and bugs that need to be addressed. &lt;em class=&quot;markup--em markup--p-em&quot;&gt;Scaling with single node databases like Mysql beyond a certain point has extremely high operational overhead.&lt;/em&gt;&lt;/div&gt;&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;NoSql databases such as Cassandra, Riak, MongoDB etc offer scalability and high availability but at the expense of data consistency. That might be ok for some social media or consumer applications where the dollar value of individual transaction is very small. But not in enterprise applications where the correctness of each transaction is worth several thousands of dollars. In enterprise applications, we need distributed data to behave the same way that we are used to with single node databases.&lt;/div&gt;&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;&lt;p class=&quot;graf graf--p&quot; name=&quot;9098&quot;&gt;Let us look at some common correctness issues that crop up with distributed data.&lt;/p&gt;&lt;h4 class=&quot;graf graf--h4&quot; name=&quot;ceb1&quot;&gt;Example 1&amp;nbsp;: A distributed on line store with servers in San Francisco, New York and&amp;nbsp;Paris.&lt;/h4&gt;&lt;p class=&quot;graf graf--p&quot; name=&quot;e2d1&quot;&gt;Each server has 2 tables products and inventory with the following data.&lt;br /&gt;&lt;em class=&quot;markup--em markup--p-em&quot;&gt;Products&lt;/em&gt;:(product)&lt;br /&gt;&amp;nbsp;widget1&lt;br /&gt;&amp;nbsp;widget2&lt;br /&gt;&lt;em class=&quot;markup--em markup--p-em&quot;&gt;Inventory:&lt;/em&gt; (product, count):&lt;br /&gt;widget1,6&lt;br /&gt;widget2,1&lt;/p&gt;&lt;p class=&quot;graf graf--p&quot; name=&quot;a60c&quot;&gt;Customer Jose connects to server in San Francisco and buys widget2 at time t1. At time t2, Customer Pierre connects to a server in Paris and also buys widget2. Assume t2 &amp;gt; t1 but t2-t1 is small.&lt;/p&gt;&lt;p class=&quot;graf graf--p&quot; name=&quot;2794&quot;&gt;Expected Behavior&amp;nbsp;: Jose successfully completes transaction and gets the product. Since inventory of widget2 is now zero, Pierre’s transaction is aborted.&lt;br /&gt;Actual Behavior (in an eventually consistent system): Both transactions complete. But only one of the customers gets the product. The other customer is later sent an apologetic email that widget2 is out of stock.&lt;/p&gt;&lt;/div&gt;&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;&lt;div dir=&quot;ltr&quot; trbidi=&quot;on&quot;&gt;&lt;h4 class=&quot;graf graf--h4&quot; name=&quot;c3da&quot;&gt;Example 2: A distributed document sharing system with servers in New York, London,&amp;nbsp;Tokyo&lt;/h4&gt;&lt;p class=&quot;graf graf--p&quot; name=&quot;eb16&quot;&gt;Operation1: In London, User X creates a new empty document marked private.&lt;br /&gt;Operation2. User X makes update 1 to document.&lt;br /&gt;Operation3: User X deletes update 1.&lt;br /&gt;Operation4: User X makes update 2.&lt;br /&gt;Operation5: User X changes the document from private to public.&lt;br /&gt;Due to network issues, only operations 1,2, 5 reach Tokyo. 3 and 4 do not.&lt;br /&gt;In Tokyo, User Y tries to read the shared document.&lt;/p&gt;&lt;p class=&quot;graf graf--p&quot; name=&quot;4c40&quot;&gt;Expected behavior: The document status is private and Y cannot read the document.&lt;br /&gt;Actual behavior: Y is able to read the document but an incorrect version. The document has update1 which is deleted and is missing update2 which needs to be there.&lt;/p&gt;&lt;p class=&quot;graf graf--p&quot; name=&quot;4371&quot;&gt;The problems above are known as consistency issues. Different clients are seeing different views of the data. What is the correct view&amp;nbsp;?&lt;/p&gt;&lt;p class=&quot;graf graf--p&quot; name=&quot;78db&quot;&gt;Consistency here refers to C in the CAP theorem, not the C in ACID. Here &lt;strong class=&quot;markup--strong markup--p-strong&quot;&gt;Consistency means every thread in a concurrent application correctly reads the most recent write at that point in time.&lt;/strong&gt;&lt;/p&gt;&lt;p class=&quot;graf graf--p&quot; name=&quot;cc37&quot;&gt;How do you fix the above issues&amp;nbsp;? In a single node database, Example1 can be fixed by locking the row in the inventory table during update and Example2 is not even an issue because all the data is in one node. But in a distributed application data might be split across shards and shards replicated for high availability. User of the system might connect to any shard/server and read/write data. With NoSql databases, the application has to handle any in consistencies.&lt;/p&gt;&lt;p class=&quot;graf graf--p&quot; name=&quot;faa5&quot;&gt;In traditional RDBMSs&amp;nbsp;, database developers are given a knob called isolation level to control what concurrent threads can read. &lt;a class=&quot;markup--anchor markup--p-anchor&quot; data-href=&quot;http://khangaonkar.blogspot.com/2010/11/database-isolation.html&quot; href=&quot;http://khangaonkar.blogspot.com/2010/11/database-isolation.html&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;In this old blog I explain what isolation levels are&lt;/a&gt;. The safest isolation level is the SERIALIZABLE where the database behaves as if the transactions were executing in a serial order with no overlap, even though in reality they are executing concurrently. Most developers use the default isolation level which is generally READ_COMMITTED OR READ_REPEATABLE. In reality, these isolation levels are poorly documented and implemented differently by different vendors. The result is that in highly concurrent applications, there are consistency bugs even in traditional single node RDBMs. In a distributed database with data spread across shards and replicated for read scalability, the problem is compounded further. Most NoSql vendors punt the problem by claiming eventual consistency, meaning if there are no writes for a while, eventually all reads on all nodes will read the last write.&lt;/p&gt;&lt;p class=&quot;graf graf--p&quot; name=&quot;2683&quot;&gt;Consistency is often confused with isolation, which describes how the database behave under concurrent execution of the transactions.&lt;strong class=&quot;markup--strong markup--p-strong&quot;&gt; At the safest isolation level, the database behaves as if the transactions were executing in serial order, even though in reality they are executing concurrently. At the safest consistency level, every thread in a concurrent application correctly reads the most recent write.&lt;/strong&gt; But most database documentations are not clear on how to achieve this in an application.&lt;/p&gt;&lt;p class=&quot;graf graf--p&quot; name=&quot;df11&quot;&gt;The problems in examples 1 and 2 would not occur if those applications/databases had the notion of a global transaction order with respect to real time. In example 1, Pierre’s transaction at t2 should see the inventory as 0 because a transaction at t1 &amp;lt;t2 set it to zero. In example 2, Y should only be able to read upto operation2&amp;nbsp;. It should not be able to read operation5 without operations 3,4 which occured before 5.&lt;/p&gt;&lt;p class=&quot;graf graf--p&quot; name=&quot;af53&quot;&gt;In database literature, the term for this requirement is called “Strict Serializability” or sometimes “external consistency”. Since this technical definitions can be confusing, it is often referred to as &lt;strong class=&quot;markup--strong markup--p-strong&quot;&gt;strong consistency&lt;/strong&gt;.&lt;/p&gt;&lt;p class=&quot;graf graf--p&quot; name=&quot;beb5&quot;&gt;2 research papers that have been around for a while provide answers on how this problems might be fixed. The papers are the &lt;a class=&quot;markup--anchor markup--p-anchor&quot; data-href=&quot;https://static.googleusercontent.com/media/research.google.com/en//archive/spanner-osdi2012.pdf&quot; href=&quot;https://static.googleusercontent.com/media/research.google.com/en//archive/spanner-osdi2012.pdf&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;Spanner&lt;/a&gt; paper and the &lt;a class=&quot;markup--anchor markup--p-anchor&quot; data-href=&quot;http://cs.yale.edu/homes/thomson/publications/calvin-sigmod12.pdf&quot; href=&quot;http://cs.yale.edu/homes/thomson/publications/calvin-sigmod12.pdf&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;Calvin&lt;/a&gt; paper.&lt;/p&gt;&lt;p class=&quot;graf graf--p&quot; name=&quot;f2ba&quot;&gt;Their approach is solving the problem can summarized as follows:&lt;br /&gt;1. timestamp transactions with something that reflect their occurrence in real time&lt;br /&gt;2. Order transactions based on timestamp&lt;br /&gt;3. Commit transactions in the above order.&lt;/p&gt;&lt;p class=&quot;graf graf--p&quot; name=&quot;221f&quot;&gt;But the details of how they do it are significantly different. Let us look at how they do it.&lt;/p&gt;&lt;h4 class=&quot;graf graf--h4&quot; name=&quot;30bc&quot;&gt;&lt;a class=&quot;markup--anchor markup--h4-anchor&quot; data-href=&quot;https://static.googleusercontent.com/media/research.google.com/en//archive/spanner-osdi2012.pdf&quot; href=&quot;https://static.googleusercontent.com/media/research.google.com/en//archive/spanner-osdi2012.pdf&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;Spanner paper from&amp;nbsp;Google&lt;/a&gt;&lt;/h4&gt;&lt;p class=&quot;graf graf--p&quot; name=&quot;a6a6&quot;&gt;Spanner is database built at Google and &lt;a class=&quot;markup--anchor markup--p-anchor&quot; data-href=&quot;https://static.googleusercontent.com/media/research.google.com/en//archive/spanner-osdi2012.pdf&quot; href=&quot;https://static.googleusercontent.com/media/research.google.com/en//archive/spanner-osdi2012.pdf&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;the paper&lt;/a&gt; describes the motivation and design of Spanner. Spanners approach involves&lt;br /&gt;1. The use of atomic clocks and GPS to synchronize clocks across hosts in different regions and the true time API to give accurate time across nodes, regions or continent.&lt;br /&gt;2. For a read/write transaction, spanner calls the true time API to get a timestamp. To address overlaps between transactions that are close to each other, the timestamp is assigned after locks are acquired and before they are released.&amp;nbsp;&lt;br /&gt;3. The commit order equals timestamp order.&lt;br /&gt;4. Read for particular timestamp is sent to any shard/replica that has the data at that timestamp.&lt;br /&gt;5. Read without timestamp (latest read) are serviced by assigning a timestamp.&lt;br /&gt;6. Writes that cross multiple shards use two phase commit.&lt;br /&gt;And of course,&lt;br /&gt;7. It can scale horizontally to 1000s of nodes by sharding.&lt;br /&gt;8. Each shard is replicated.&lt;br /&gt;And most importantly,&amp;nbsp;&lt;br /&gt;9. Even though, it is a key value store, it provide SQL support to make it easy for application programmers.&lt;br /&gt;&lt;a class=&quot;markup--anchor markup--p-anchor&quot; data-href=&quot;https://www.cockroachlabs.com/&quot; href=&quot;https://www.cockroachlabs.com/&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;CockroachDb&lt;/a&gt; and &lt;a class=&quot;markup--anchor markup--p-anchor&quot; data-href=&quot;https://www.yugabyte.com/&quot; href=&quot;https://www.yugabyte.com/&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;Yugabyte&lt;/a&gt; are 2 commercial databases based on spanner.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;&lt;p class=&quot;graf graf--p&quot; name=&quot;1229&quot;&gt;&lt;strong class=&quot;markup--strong markup--p-strong&quot;&gt;&lt;a class=&quot;markup--anchor markup--p-anchor&quot; data-href=&quot;http://cs.yale.edu/homes/thomson/publications/calvin-sigmod12.pdf&quot; href=&quot;http://cs.yale.edu/homes/thomson/publications/calvin-sigmod12.pdf&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;Calvin Paper&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p class=&quot;graf graf--p&quot; name=&quot;1229&quot;&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;The &lt;a class=&quot;markup--anchor markup--p-anchor&quot; data-href=&quot;http://cs.yale.edu/homes/thomson/publications/calvin-sigmod12.pdf&quot; href=&quot;http://cs.yale.edu/homes/thomson/publications/calvin-sigmod12.pdf&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;Calvin paper&lt;/a&gt; addresses the above problem using distributed consensus protocols like &lt;a class=&quot;markup--anchor markup--p-anchor&quot; data-href=&quot;https://raft.github.io/&quot; href=&quot;https://raft.github.io/&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;Raft&lt;/a&gt; or Paxos.&amp;nbsp;&lt;br /&gt;1. Every transaction has to first go through distributed consensus and secure a spot in a linear replication log.&amp;nbsp;&lt;br /&gt;2. One can view the index in the log as the timestamp.&amp;nbsp;&lt;br /&gt;3. The committed entries in the replication log are then executed in the exact same serial order by every node in the distributed database.&amp;nbsp;&lt;br /&gt;4. Since the transaction log is replicated to every shard, it does not need or use two phase commit. In a transaction involving multiple shards, if a shard dies before committing a particular transaction, then on restart it just has to execute the uncommitted transaction from it replication log.&lt;br /&gt;5. No dependency on wall clocks or time API.&lt;br /&gt;6. No two phase commit.&lt;br /&gt;7. No mention of SQL support.&lt;/p&gt;&lt;p class=&quot;graf graf--p&quot; name=&quot;2840&quot;&gt;&amp;nbsp;&lt;a class=&quot;markup--anchor markup--p-anchor&quot; data-href=&quot;https://fauna.com/&quot; href=&quot;https://fauna.com/&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;FaunaDb&lt;/a&gt; is an example of a database based on Calvin.&lt;/p&gt;&lt;p class=&quot;graf graf--p&quot; name=&quot;0c62&quot;&gt;This class of databases that offer horizontal scalability on a global scale without sacrificing consistency is also called NewSql.&amp;nbsp;&lt;/p&gt;&lt;p class=&quot;graf graf--p&quot; name=&quot;ce36&quot;&gt;In summary, if you are a building a globally distributed application that needs strong consistency, doing it on your own with SQL or NoSql database can be non trivial. Consistency is hard enough in a single node database. But on a distributed database, consistency bugs are harder to troubleshoot and even harder to fix. You might want to consider one of the NewSql databases to make life easier. Review the Spanner and Calvin papers to understand the architectural choices that are available. This will help you pick a database that is right for you. Spanner and Calvin papers have been around for almost a decade. But they have become more relevant now as real databases based on them become more popular. Most importantly understand what is consistency is and apply it, for lack of which can cause severe correctness bugs in your application.&amp;nbsp;&lt;/p&gt;&lt;p class=&quot;graf graf--p&quot; name=&quot;eecf&quot;&gt;&lt;strong class=&quot;markup--strong markup--p-strong&quot;&gt;References:&lt;/strong&gt;&lt;/p&gt;&lt;p class=&quot;graf graf--p&quot; name=&quot;eecf&quot;&gt;&lt;a class=&quot;markup--anchor markup--li-anchor&quot; data-href=&quot;https://static.googleusercontent.com/media/research.google.com/en//archive/spanner-osdi2012.pdf&quot; href=&quot;https://static.googleusercontent.com/media/research.google.com/en//archive/spanner-osdi2012.pdf&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;The Spanner paper&lt;/a&gt;&lt;/p&gt;&lt;p class=&quot;graf graf--p&quot; name=&quot;eecf&quot;&gt;&lt;a class=&quot;markup--anchor markup--li-anchor&quot; data-href=&quot;http://cs.yale.edu/homes/thomson/publications/calvin-sigmod12.pdf&quot; href=&quot;http://cs.yale.edu/homes/thomson/publications/calvin-sigmod12.pdf&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;The Calvin paper&lt;/a&gt;&lt;/p&gt;&lt;p class=&quot;graf graf--p&quot; name=&quot;d0a8&quot;&gt;&lt;a class=&quot;markup--anchor markup--p-anchor&quot; data-href=&quot;https://fauna.com/blog/introduction-to-transaction-isolation-levels&quot; href=&quot;https://fauna.com/blog/introduction-to-transaction-isolation-levels&quot; rel=&quot;noopener&quot; target=&quot;_blank&quot;&gt;Consistency and Isolation&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/8285583592047900270'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/8285583592047900270'/><link rel='alternate' type='text/html' href='http://khangaonkar.blogspot.com/2020/07/globally-distributed-transactions.html' title='Building Globally Distributed Applications'/><author><name>Manoj Khangaonkar</name><uri>http://www.blogger.com/profile/13925926828045222969</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-5008017311510568944.post-2503094615437473616</id><published>2019-01-20T18:31:00.000-08:00</published><updated>2024-07-20T16:48:36.924-07:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Microservices architecture web cloud distributed programming Dropwizard SpringBoot Docker Kubernetes"/><title type='text'>A Microservices Introduction</title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
Modern distributed applications are built as a suite of microservices. In this blog we discuss the characteristics of microservices. We will also compare microservices to its predecessors like SOA and monolithic applications. We point out the benefits and downsides of a microservices architecture.&lt;br /&gt;
&lt;br /&gt;
&lt;h2 style=&quot;text-align: left;&quot;&gt;
1.0 Introduction&lt;/h2&gt;
&lt;br /&gt;
Let us start with a little bit of history and go back to late 90s or early 2000&#39;s. Web applications were monolithic. A single web container would serve the entire application. Even worse, a single web container would serve multiple applications. Not only was this not scalable, it was a development and maintenance nightmare. A single bug could bring multiple applications down. And there was an ownership issue. You had multiple teams/developers contributing code. When there was a bug, the ownership was not clear and bugs would bounce around among developers.&lt;br /&gt;
&lt;br /&gt;
Around mid 2000&#39;s the new buzz word was service oriented architecture SOA. This was promoted by large web application server companies. See my blog on &lt;a href=&quot;http://khangaonkar.blogspot.com/2010/02/service-oriented-architecture.html&quot;&gt;SOA&lt;/a&gt; written in 2010. SOA encouraged number of good design philosophies such as interface based programming, loosely coupled applications, asynchronous interaction. REST, XML,JSON and messaging platforms enabled SOA. SOA was a big improvement, but the tools and deployment technologies were still heavyweight.&lt;br /&gt;
&lt;br /&gt;
The microservices architecture is the next step in evolution further improving the ideas from SOA.&lt;br /&gt;
&lt;br /&gt;
Many dismiss microservices as another buzz word. But having developed real world applications using tools listed in section 3.0, I see real value and benefit in this architecture.&lt;br /&gt;
&lt;br /&gt;
&lt;h2 style=&quot;text-align: left;&quot;&gt;
2.0 Description&lt;/h2&gt;
&lt;br /&gt;
The main idea around microservices is that large complex systems are easier to build, maintain and scale using independently built and owned smaller services that work together.&lt;br /&gt;
&lt;br /&gt;
Each microservice is a modular fine grained application providing a specific service. Let us say you have an application that has a UI , authentication, Apis for customer info, Apis for uploading documents, Apis for analytics. You may have a microservice for the UI, a microservice for customer apis, a document upload microservice, an analytics microservice.&lt;br /&gt;
&lt;br /&gt;
A microservice is fully functional.&lt;br /&gt;
&lt;br /&gt;
A microservice performs one specific business or IT function.&lt;br /&gt;
&lt;br /&gt;
The development of the microservice can be done independently.&lt;br /&gt;
&lt;br /&gt;
A microservice runs as its own process.&lt;br /&gt;
&lt;br /&gt;
A microservice communicates using common protocols such as REST/Http.&lt;br /&gt;
&lt;br /&gt;
A microservice offers services via its Apis. It can communicate with other microservices using their APIs.&lt;br /&gt;
&lt;br /&gt;
A microservice is deployable to production independently.&lt;br /&gt;
&lt;br /&gt;
When your application has multiple microservices, each could be developed in a different or the most suitable programming language or framework suited for that service.&lt;br /&gt;
&lt;br /&gt;
A microservice should scale horizontally by just running more instances of the microservice.&lt;br /&gt;
&lt;br /&gt;
Testing, bug fixing, performance tuning etc on the microservice should happen independently without affecting other microservices.&lt;br /&gt;
&lt;br /&gt;
The above listed characteristics make it easier to build large complex systems.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;h2 style=&quot;text-align: left;&quot;&gt;
3.0 Enabling Technologies&lt;/h2&gt;
&lt;br /&gt;
A number of newer frameworks have made building microservices easier.&lt;br /&gt;
&lt;br /&gt;
For Java programmers, &lt;a href=&quot;http://dropwizard.io/&quot;&gt;Dropwizard&lt;/a&gt; and &lt;a href=&quot;http://spring.io/projects/spring-boot&quot;&gt;SpringBoot&lt;/a&gt;&amp;nbsp;are very useful frameworks for building microservices. The old way was monolithic application servers like websphere, weblogic , jboss etc. Dropwizard and SpringBoot turn the table by embedding the web server within your java application. Development is much easier as you are developing a plain java application with a main method. The entire microservice is packaged in one jar and can be run with the java -jar command. For additional information, please read my blog &lt;a href=&quot;http://khangaonkar.blogspot.com/2018/04/tomcat-vs-dropwizard.html&quot;&gt;comparing Dropwizard to Tomcat&lt;/a&gt;. For Javascript, python and other languages there are similar frameworks.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
To start with microservices, a framework as mentioned above is all you need. Once you have developed and use several microservices, the following platforms may be useful.&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://www.docker.com/&quot;&gt;Docker&lt;/a&gt; is a containerization technology that makes it easier to manage production deployments. This is of interest for a dev-ops person who has to roll out services to production.&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://kubernetes.io/&quot;&gt;Kubernetes&lt;/a&gt; is platform for automation, deployment and scaling of containerized applications.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;h2 style=&quot;text-align: left;&quot;&gt;
4.0 Disadvantages&lt;/h2&gt;
&lt;br /&gt;
For smaller business and smaller applications, the overhead of&amp;nbsp; many microservices could be a problem. If your infrastructure is one or two $20 per month VMs on AWS (or other cloud providers) you will not have enough memory/cpu/disk for multiple microservices.&lt;br /&gt;
&lt;br /&gt;
The increased network communication is a cost.&lt;br /&gt;
&lt;br /&gt;
Each microservice is its own process. The remote calls have a serialization/deserialization cost.&lt;br /&gt;
&lt;br /&gt;
&lt;h2 style=&quot;text-align: left;&quot;&gt;
5.0 Conclusion&lt;/h2&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
Microservices are a logical next step in the evolution of&amp;nbsp; the development of complex applications.&lt;br /&gt;
They are a best practice. But they are not a silver bullet that solve every problem.&lt;br /&gt;
&lt;br /&gt;
&lt;h2 style=&quot;text-align: left;&quot;&gt;&lt;br /&gt;&lt;/h2&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/2503094615437473616'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/2503094615437473616'/><link rel='alternate' type='text/html' href='http://khangaonkar.blogspot.com/2019/01/a-microservices-introduction.html' title='A Microservices Introduction'/><author><name>Manoj Khangaonkar</name><uri>http://www.blogger.com/profile/13925926828045222969</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author></entry><entry><id>tag:blogger.com,1999:blog-5008017311510568944.post-955272053572661669</id><published>2018-12-02T08:23:00.000-08:00</published><updated>2018-12-02T08:23:56.834-08:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Kafka Messaging BigData Analytics java Distributed applications"/><title type='text'>Apache kafka Streams </title><content type='html'>&lt;div dir=&quot;ltr&quot; style=&quot;text-align: left;&quot; trbidi=&quot;on&quot;&gt;
Apache Kafka is a popular distributed messaging and streaming open source system. A key differentiator for Kafka is that its distributed broker architecture makes it highly scalable. Earlier versions of Kafka were more about messaging. I have a number of blogs on Kafka messaging some of which are listed below in the related blogs section.&lt;br /&gt;
&lt;br /&gt;
This blog introduces Kafka streams which builds on messaging.&lt;br /&gt;
&lt;br /&gt;
&lt;h2 style=&quot;text-align: left;&quot;&gt;
1.0 Introduction&lt;/h2&gt;
&lt;br /&gt;
In a traditional Kafka producer/consumer application, producers write messages to a topic and consumers consume the messages. The consumer may process the message and then write it to a database , filesystem or even discard it. For a consumer to write the message back to another topic, it has to create a producer.&lt;br /&gt;
&lt;br /&gt;
Kafka streams is a higher level library that lets you build a processing pipeline on streams of messages where each stream processor reads a message, does some analytics such as counting, categorizing , aggregation etc and then potentially writes a result back to another topic.&lt;br /&gt;
&lt;br /&gt;
&lt;h2 style=&quot;text-align: left;&quot;&gt;
2.0 Use Cases&lt;/h2&gt;
&lt;br /&gt;
Analytics from e-commerce site usage.&lt;br /&gt;
&lt;br /&gt;
Analytics from any distributed application.&lt;br /&gt;
&lt;br /&gt;
Distributed processing of any kind of event or data steams.&lt;br /&gt;
&lt;br /&gt;
Transforming from monolithic to micro-services architecture.&lt;br /&gt;
&lt;br /&gt;
Moving away from database intensive architectures.&lt;br /&gt;
&lt;br /&gt;
&lt;h2 style=&quot;text-align: left;&quot;&gt;
3.0 When to use Kafka streams ?&lt;/h2&gt;
&lt;br /&gt;
If yours is a traditional messaging application, where you need the broker to hold on to messages till they get processed by a consumer, then the producer/consumer framework might be suitable. Here kafka competes with ActiveMQ, Websphere MQ and other traditional message brokers. Here the processing of each message is independent of other messages.&lt;br /&gt;
&lt;br /&gt;
If yours is a analytics style application, where you have do different forms of counting, aggregation, slicing /dicing on a stream of data, then Kafka streams might be an appropriate library. Here the processing is for a set of messages in the stream. In this space.&amp;nbsp; Kafka competes with analytics frameworks like Apache Spark, Storm,&amp;nbsp; splunk etc.&lt;br /&gt;
&lt;br /&gt;
If you were to use producers/consumers for an analytics style application, you would end up creating many producers/consumer, you would probably have to read and write a database several time, you would need to maintain in memory state and probably use a third party library for analytics primitives. Kafka streams library makes all this easier for you.&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;
4.0 Features&lt;/h2&gt;
&lt;br /&gt;
Some key features of Kafka streams are:&lt;br /&gt;
&lt;br /&gt;
Provides an API for stream processing primitives such counting, aggregation, categorization etc. API supports timing windows.&lt;br /&gt;
&lt;br /&gt;
Message processing is one at the time. In producer/consumer , messages are generally processed in batches.&lt;br /&gt;
&lt;br /&gt;
Fault tolerant local state is provided by the library. In consumers, any state has to be managed by the application.&lt;br /&gt;
&lt;br /&gt;
Supports exactly once&amp;nbsp; or once and only once message message delivery. In producer/consumer, it is at least once delivery.&lt;br /&gt;
&lt;br /&gt;
No need to deal will lower level messaging concepts like partitions, producers, consumers, polling.&lt;br /&gt;
&lt;br /&gt;
Self contained complete library that handles both messaging and processing. No need for other third party libraries like Spark.&lt;br /&gt;
&lt;br /&gt;
&lt;h2 style=&quot;text-align: left;&quot;&gt;
5.0 Concepts&amp;nbsp;&lt;/h2&gt;
&lt;br /&gt;
A stream is an unbounded sequence of Kafka messages on a topic.&lt;br /&gt;
&lt;br /&gt;
A stream processor is a piece of code that gets a message, does some processing on it, perhaps stores some in memory state and then writes to another topic for processing by another processor. This is also known as a node.&lt;br /&gt;
&lt;br /&gt;
A stream application is a set of processors where the output of one processor is further processed by one or more other processors. A stream application can depicted as graph with the processors as vertexes and streams/topics as edges.&lt;br /&gt;
&lt;br /&gt;
A source processor is the first node in the topology. It has no upstream processors and gets messages from a topic. A sink processor has no downstream processors and will typically write a result somewhere.&lt;br /&gt;
&lt;br /&gt;
The figure below shows a sample application topology&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_wpuw-b4DdAlWEAudM-XnOdG3f-WTpGNyolPscghrrIiacavTQnLSMvgIsva7NTLdKg-BuPfinEQINb_HkofWbOHQNOOsr4WtaCiV_CQY6cruQ9Tlf5vPIV1zsmYSipLbu45zU43LSQA/s1600/Kafka+Stream+Topology.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;720&quot; data-original-width=&quot;960&quot; height=&quot;300&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_wpuw-b4DdAlWEAudM-XnOdG3f-WTpGNyolPscghrrIiacavTQnLSMvgIsva7NTLdKg-BuPfinEQINb_HkofWbOHQNOOsr4WtaCiV_CQY6cruQ9Tlf5vPIV1zsmYSipLbu45zU43LSQA/s400/Kafka+Stream+Topology.jpg&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;h2 style=&quot;text-align: left;&quot;&gt;
6.0 Programming model&lt;/h2&gt;
&lt;br /&gt;
2 core programming models. Below are some sample code snippets.&lt;br /&gt;
&lt;br /&gt;
&lt;h3 style=&quot;text-align: left;&quot;&gt;
6.1 Streams DSL&lt;/h3&gt;
&lt;div&gt;
This is a higher level API build on top of the processor API. Great for beginners.&lt;br /&gt;
&lt;br /&gt;
KStream models the stream of messages. KTable is the in-memory story. You can convert from stream to table and vice versa.&lt;/div&gt;
&lt;br /&gt;
Example: Simple analytics on a stream of pageviews from e-commerce site&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;color: purple;&quot;&gt;// consume from a topic&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: purple;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;color: purple;&quot;&gt;StreamBuilder builder = new StreamBuilder() ;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: purple;&quot;&gt;KStream&lt;string tring=&quot;&quot;&gt; pageViewlines = builder.stream(&quot;someTopic&quot;) ;&lt;/string&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: purple;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;color: purple;&quot;&gt;// From each line extract productid and create a table key=productid,value=count&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: purple;&quot;&gt;// We get page view count by product&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: purple;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;color: purple;&quot;&gt;KTable&lt;string ong=&quot;&quot;&gt; productCounts = pageViewlines.flatMapValues(value-&amp;gt;getProduct(value))&lt;/string&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: purple;&quot;&gt;.groupBy((key,value)-&amp;gt;value)&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: purple;&quot;&gt;.count() ;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;color: purple;&quot;&gt;// write the running counts to another topic or storage&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: purple;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;color: purple;&quot;&gt;productCounts.toStream.to(&quot;productCountsTopic&quot;,Produced.with(serdes.String(),serdes.Long()) ;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;h3 style=&quot;text-align: left;&quot;&gt;
6.2 Processor API&lt;/h3&gt;
&lt;br /&gt;
same example using processor API&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;color: purple;&quot;&gt;public class ProductFromPageViewProcessor implements Processor&lt;string tring=&quot;&quot;&gt; {&lt;/string&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: purple;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;color: purple;&quot;&gt;&amp;nbsp; private KeyValueStore&lt;string ong=&quot;&quot;&gt; pcountStore ;&lt;/string&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: purple;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;color: purple;&quot;&gt;&amp;nbsp; &amp;nbsp;// Do any initialization here&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: purple;&quot;&gt;&amp;nbsp; &amp;nbsp;// such as loading stores or scheduling punctuate&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: purple;&quot;&gt;&amp;nbsp; &amp;nbsp;public void init(ProcessorContext context) {&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: purple;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;color: purple;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;// get the store that will store counts&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: purple;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;pcountStore = (KeyValueStore)context.getStateStore(&quot;pcounts&quot;) ;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: purple;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;color: purple;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // schedule a punctuate to to periodically send the product counts to a downstream processor&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: purple;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // every 5 secs&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: purple;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;color: purple;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; context.schedule(5000,PunctuationType.STREAM_TIME,(timestamp)-&amp;gt;{&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: purple;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;color: purple;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;// iterate over all values in the pcountStore&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: purple;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; KeyValueIter iter = pCountStore.all()&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: purple;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;while(iter.hasNext()) {&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: purple;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; KeyValue&lt;string ong=&quot;&quot;&gt; val = iter.next() ;&lt;/string&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: purple;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; context.forward(val.key,val.value) ;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: purple;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;}&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: purple;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;color: purple;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;context.commit() ;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: purple;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;color: purple;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;} ;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: purple;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;color: purple;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;color: purple;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp;// Called once for every line or message on the consumed topic&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: purple;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; public void process(String k, String line) {&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: purple;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;String productId = getProductId(line) ;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: purple;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;Long count = pCountStore.get(productId) ;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: purple;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (count == null) {&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: purple;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; pCountStore.put(productId,1) ;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: purple;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; } else {&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: purple;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;pCountStore.put(productId,count+1) ;&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: purple;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: purple;&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: purple;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;color: purple;&quot;&gt;&amp;nbsp; &amp;nbsp; }&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: purple;&quot;&gt;}&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;h2 style=&quot;text-align: left;&quot;&gt;
7.0 Conclusion&lt;/h2&gt;
&lt;br /&gt;
As you can see from both API examples, it is about processing streams of data, doing some analytics and producing results. No need to poll or deal with lower level details like partitions and consumers. The streams model moves you away from legacy database intensive architectures, where data is written to a database first and then slow inefficient queries try to do analytics.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Some disadvantages of Kafka Streams are:&lt;br /&gt;
&lt;br /&gt;
You are tied to Kafka and have to go through a Kafka topic. Other Streaming libraries like Spark are more generic and might have more analytics features.&lt;br /&gt;
&lt;br /&gt;
There are no ways to pause and resume a stream. If&amp;nbsp; load suddenly increases or you want to pause the system for maintenance, there is no clean mechanism. In producer/consumer, there is explicit pause/resume API. Other streaming libraries also have some flow control mechanisms.&lt;br /&gt;
&lt;br /&gt;
&lt;h2 style=&quot;text-align: left;&quot;&gt;
8.0 Related Blogs&lt;/h2&gt;
&lt;div&gt;
&lt;a href=&quot;http://khangaonkar.blogspot.com/2014/04/apache-kafka-introduction-should-i-use.html&quot;&gt;8.1 Apache Kafka Introduction&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;a href=&quot;http://khangaonkar.blogspot.com/2014/05/apache-kafka-java-tutorial.html&quot;&gt;8.2 Apache kafka Basic tutorial&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;a href=&quot;http://khangaonkar.blogspot.com/2015/01/apache-kafka-java-tutorial-3-once-and.html&quot;&gt;8.3 Apache Kafka once and once delivery&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/955272053572661669'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5008017311510568944/posts/default/955272053572661669'/><link rel='alternate' type='text/html' href='http://khangaonkar.blogspot.com/2018/12/apache-kafka-streams.html' title='Apache kafka Streams '/><author><name>Manoj Khangaonkar</name><uri>http://www.blogger.com/profile/13925926828045222969</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_wpuw-b4DdAlWEAudM-XnOdG3f-WTpGNyolPscghrrIiacavTQnLSMvgIsva7NTLdKg-BuPfinEQINb_HkofWbOHQNOOsr4WtaCiV_CQY6cruQ9Tlf5vPIV1zsmYSipLbu45zU43LSQA/s72-c/Kafka+Stream+Topology.jpg" height="72" width="72"/></entry></feed>