<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Abhishek Sah — Blog]]></title><description><![CDATA[Infrastructure & Backend Engineer]]></description><link>https://absh.dev</link><generator>GatsbyJS</generator><lastBuildDate>Fri, 05 Jun 2026 13:55:51 GMT</lastBuildDate><item><title><![CDATA[Executable Onboarding: From Docs to Agents]]></title><description><![CDATA[Replacing a long README with a small agent that spins up a complex service locally and drives its tests — onboarding as executable code.]]></description><link>https://absh.dev/frontier-sandbox-claude-command/</link><guid isPermaLink="false">https://absh.dev/frontier-sandbox-claude-command/</guid><pubDate>Wed, 20 May 2026 10:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Most backend services of any real size are painful to bring up locally, and once they are running, testing your changes by calling the actual APIs and walking through real flows is just as tedious. &lt;strong&gt;With the rise of LLMs, the better answer is not a longer README. It is a small agent.&lt;/strong&gt; That agent can run the setup itself, remember what it has done, and follow instructions in plain English, both for bringing the service up and for driving the tests on top of it. This post is about one such agent, built for the service I work on day to day.&lt;/p&gt;
&lt;p&gt;These days at work, I am mostly building on &lt;a href=&quot;https://github.com/raystack/frontier&quot;&gt;Frontier&lt;/a&gt;, an open source identity and billing platform from Raystack. On the identity side, it owns the org, project, and service-account model, and it manages roles and permissions through an authorization graph backed by &lt;a href=&quot;https://github.com/authzed/spicedb&quot;&gt;SpiceDB&lt;/a&gt;. On the billing side, it tracks plans, subscriptions, features, usage, and invoices, and it integrates with a payment provider so the same organization model handles both “who can do what” and “who pays for what.” In practice, a B2B SaaS team using Frontier does not have to rebuild the usual primitives from scratch, like tenant isolation, role-based access, SSO, service accounts for API traffic, plans wired to feature entitlements, metered usage, and invoicing. All of those come out of the box, with one consistent org model underneath. That is what a mature platform here is supposed to do: take the parts every enterprise product re-implements and turn them into something the application team can configure rather than write.&lt;/p&gt;
&lt;p&gt;That breadth is what makes Frontier useful, and it is also what makes it expensive to test locally. A single change can travel through identity, authz, and billing in the same request path. This post is about a Claude Code plugin I wrote to close that gap.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;The Cost of a Real Local Test&lt;/h3&gt;
&lt;p&gt;Frontier needs PostgreSQL (two databases, its own and SpiceDB’s), a SpiceDB instance, migrations applied, and the server built from source against the right config. Once the server is running, you still have to authenticate, and there is more than one way to do it depending on who you are calling as: mail OTP for regular users, headers for service users, and a separate super-admin path tied to the config file. Each path gives you a different session, and each session can only call a specific subset of RPCs.&lt;/p&gt;
&lt;p&gt;To test a change touching project role assignment end to end, you bring up the stack, run the OTP flow, capture the cookie, create an org, a project, a service user, and a few users with different roles, and only then can you call the RPC the change is actually about. On a good day, an hour is gone before you send the first meaningful request. On a bad day (a fresh laptop, a stale config, a SpiceDB version mismatch, a forgotten migration), it has been known to take a couple of days before someone gets the stack working end to end.&lt;/p&gt;
&lt;p&gt;So most engineers do not do this. They write a unit test, watch CI go green, and ship. But a unit test does not catch the kinds of bugs a change like this usually causes: stale SpiceDB relations after a role update, a service user RPC that breaks on a missing header, a role assignment that reads correctly in code but leaves incomplete relations in the authz graph. These bugs are usually small once you find them. The painful part is that you only find them after the change has shipped.&lt;/p&gt;
&lt;p&gt;The stakes are higher here than they are for most services. For an identity and billing platform, correctness and security are the whole point of the product. A small bug in authorization means someone sees data they should not see. A small bug in billing means a customer gets an invoice that is wrong. Neither of those is something you can fix quietly in the next release. They end up in incident channels and on support tickets. Catching this kind of bug locally, before it ships, is much cheaper than catching it after.&lt;/p&gt;
&lt;h3&gt;The Plugin&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;frontier-sandbox&lt;/code&gt; is a Claude Code plugin, published in a &lt;a href=&quot;https://github.com/whoAbhishekSah/claude-marketplace&quot;&gt;small marketplace repo&lt;/a&gt;. It does two things that are usually treated separately: it takes care of the local setup, and it drives the actual RPC testing on top.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Setup.&lt;/strong&gt; One command brings up the stack: it detects an existing session and reuses it, otherwise it provisions PostgreSQL and SpiceDB (Docker by default, local install as a fallback), builds Frontier, runs migrations, and starts the server. &lt;code class=&quot;language-text&quot;&gt;rebuild&lt;/code&gt; restarts the binary without dropping data.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/45c713c0af41dd6cc31e010906e9295c/669cd/1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 27.7027027027027%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAlklEQVR42qWQ2wrDIBBEjZpEBUPMBfIiif//kVNmwTRpKaX04bCiM7O7quM4UErBvu9Sc87Ytg3rumKappN5nr9CnbLWotK2LZqmgVLqZ07fsiwgwzBIh5QSYozw3gshBME5h77vpWk9d10nVWv9DBzHUUKqsMIwimtwnZ7mV27TXlcmFBhj3ky8+8TVq9iZ8IEr/PuHD3fWgXwogW5kAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Plugin Init&quot;
        title=&quot;&quot;
        src=&quot;/static/45c713c0af41dd6cc31e010906e9295c/fcda8/1.png&quot;
        srcset=&quot;/static/45c713c0af41dd6cc31e010906e9295c/12f09/1.png 148w,
/static/45c713c0af41dd6cc31e010906e9295c/e4a3f/1.png 295w,
/static/45c713c0af41dd6cc31e010906e9295c/fcda8/1.png 590w,
/static/45c713c0af41dd6cc31e010906e9295c/efc66/1.png 885w,
/static/45c713c0af41dd6cc31e010906e9295c/c83ae/1.png 1180w,
/static/45c713c0af41dd6cc31e010906e9295c/669cd/1.png 3024w&quot;
        sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Testing.&lt;/strong&gt; Authentication is where the plugin saves the most time. Frontier’s config already supports test identities with a hardcoded OTP (useful for local and CI, switched off in production), and the plugin knows how to take advantage of that. It works with a small, predictable set of users covering the three identity types you usually need: regular users, service users, and super admins. When you ask it to issue an RPC as one of them, it runs the OTP flow against that hardcoded value, persists the cookie, and reuses the session across subsequent calls. It also reads Frontier’s proto definitions, so it can resolve request and response shapes inline and emit a &lt;code class=&quot;language-text&quot;&gt;curl&lt;/code&gt; equivalent on demand.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b57d70e3ed315cd77d73eed04050a699/42126/2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 43.24324324324324%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAABYlAAAWJQFJUiTwAAABCUlEQVR42oWS246DMAxEA4FyDZDEAboX8cD/f6PrcRdUrXbLw8iWISdjO2bfd4a2beNlWdh7zyEEnqZJ81fFGC9l1nXlcRy5bVvO85xvtxs3TaM1RKjrOq7rmo0x1zrIAFYCw0FA4GgYBgXDMfIsy1S4+D8ZAOAAQIBQtNZqPAAQakVRaHyt/5YBzDmnrT7BvczPizMvueO+78+2q6rS/wBGe38CMcOUkhxomdKd5/mLiT44SVzvG0ea5fvCbvBclqWO5C0QMCJSFyEuCqL0KSDAv3nyUR07N7KVMVy1fS4Fw/eeOBCeTlKRQEMgAc/ypGZdDJ7TW4doAa08t2R/FmLP/FjG4exqKQ9EIcciLayxxwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Show logged in users&quot;
        title=&quot;&quot;
        src=&quot;/static/b57d70e3ed315cd77d73eed04050a699/fcda8/2.png&quot;
        srcset=&quot;/static/b57d70e3ed315cd77d73eed04050a699/12f09/2.png 148w,
/static/b57d70e3ed315cd77d73eed04050a699/e4a3f/2.png 295w,
/static/b57d70e3ed315cd77d73eed04050a699/fcda8/2.png 590w,
/static/b57d70e3ed315cd77d73eed04050a699/efc66/2.png 885w,
/static/b57d70e3ed315cd77d73eed04050a699/c83ae/2.png 1180w,
/static/b57d70e3ed315cd77d73eed04050a699/42126/2.png 2884w&quot;
        sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a3ff2d293ce2de0ef36472129cbcb231/42126/3.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 30.405405405405407%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAuklEQVR42o2Q2Q6DIBBFUVDW4obGB2ri/3/krTMNbW3TtA8nM8xyuSByztj3Hdu24ZqvWNcV4zgipcTM8/yg1Ep9WZZTj/aE8w7TNHEhxogQAoyxcM7Bew+t9YmmaTi2bcsYYzjWdQ0hBAQl1lp0XceQIIkZo3m4qqr74BtUL72S85lc9X3PQjH2GIZ0ODtc2nA4scdlAdZdmNfFbwhy8bQtIaXiqFQDqRTHwl+C9MwiWv5IsdAn1PvFDTQYg39ZpGvPAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Perform an action&quot;
        title=&quot;&quot;
        src=&quot;/static/a3ff2d293ce2de0ef36472129cbcb231/fcda8/3.png&quot;
        srcset=&quot;/static/a3ff2d293ce2de0ef36472129cbcb231/12f09/3.png 148w,
/static/a3ff2d293ce2de0ef36472129cbcb231/e4a3f/3.png 295w,
/static/a3ff2d293ce2de0ef36472129cbcb231/fcda8/3.png 590w,
/static/a3ff2d293ce2de0ef36472129cbcb231/efc66/3.png 885w,
/static/a3ff2d293ce2de0ef36472129cbcb231/c83ae/3.png 1180w,
/static/a3ff2d293ce2de0ef36472129cbcb231/42126/3.png 2884w&quot;
        sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Beyond auth, the plugin takes care of the rest of the tedious testing work too. Without it, you would be hand-crafting &lt;code class=&quot;language-text&quot;&gt;curl&lt;/code&gt; calls or building Postman requests, filling in dummy payloads, looking up UUIDs you just created, reading JSON responses line by line, and then repeating the whole exercise for every edge case the API has, like invalid inputs, wrong-scope arguments, missing fields, and the occasional monkey test where you throw something deliberately strange at the endpoint to see how it reacts.&lt;/p&gt;
&lt;p&gt;The plugin handles each of those: it shapes the request from the proto, fills in identifiers it already knows about, summarizes the response, and walks through the edge cases as it goes. &lt;strong&gt;You spend your time deciding what to test, not what to type.&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;Docker Gives State, the Plugin Gives Memory&lt;/h3&gt;
&lt;p&gt;You can get most of the way there with a &lt;code class=&quot;language-text&quot;&gt;docker-compose&lt;/code&gt; file and a seed script. That is in fact what the plugin uses for its Docker mode under the hood. What that setup does not give you is memory across a working session: which identity you logged in as, which cookie is still valid, which RPC needs the super-admin path, or which entities you just created and might want to call against next.&lt;/p&gt;
&lt;p&gt;That continuity is the actual product here. You say “create an org as super admin,” then “now add user2 as a viewer on the project we just created,” and the skill figures out which project you mean, picks the identity with the right scope, and shapes the request accordingly. Docker compose can hand you a running service, but the plugin goes one step further: it hands you a running service that also keeps track of what you have already done with it in the same session.&lt;/p&gt;
&lt;h3&gt;What This Changed&lt;/h3&gt;
&lt;p&gt;A recent PR I shipped, &lt;a href=&quot;https://github.com/raystack/frontier/pull/1481&quot;&gt;#1481&lt;/a&gt;, touched project role assignment. Without the plugin, I would have run two or three scenarios by hand. With the plugin handling both the setup and the actual test calls, twenty-nine scenarios got covered before I opened the PR. I prompted and reviewed; the plugin did the running. The breakdown was nine on the happy path, seven on authorization (org owner, org admin without project role, project viewer who should be denied, unauthenticated caller, and the obvious adjacent cases), and thirteen on validation (invalid UUIDs, non-existent principals, wrong-scope roles, empty bodies). I posted the full table of what was tested and what passed as a &lt;a href=&quot;https://github.com/raystack/frontier/pull/1481#issuecomment-4153334757&quot;&gt;comment on the PR&lt;/a&gt;, so the reviewer could see at a glance what had been covered without having to ask.&lt;/p&gt;
&lt;p&gt;Each scenario ran against a clean database with the right identity, against a binary I had just rebuilt. The actual runs took seconds. The LLM proposed most of the cases itself, given the diff and the proto definitions. My job was to review the list, fix any issue that turned up during a run, ask the plugin to rebuild and restart the server, and retest. The bootstrap and the manual testing work that used to eat up that time are now effectively free.&lt;/p&gt;
&lt;p&gt;The same run also caught a real bug. In earlier local testing, some of the authorization cases had been passing when they should have failed, because SpiceDB still had permission relations left over from previous runs. The code path doing the role update only ever added new relations and never cleaned up the old ones, so the leftover state was hiding the problem. Once every run started from a clean database, the false passes went away and I could finally tell whether the fix actually worked.&lt;/p&gt;
&lt;h3&gt;The Broader Lesson&lt;/h3&gt;
&lt;p&gt;Most backend services of any real size have a version of this problem. The people who built it know in their head how to bring it up locally, and the people who joined later usually do not. There are several ways to authenticate, all obvious to the original team and confusing to everyone else, and there are dozens of small decisions about which identity should issue which call. Newer engineers and people who only contribute now and then pay for that complexity on every change, and the README, if there is one, has usually drifted from how things actually work.&lt;/p&gt;
&lt;p&gt;Turning the setup into something that actually runs the steps, rather than something that only describes them, works for a few simple reasons. It checks what is already installed instead of assuming. It can make sensible choices during setup, like which port to use or which dependency manager is available on the machine. It remembers what you have done in the current session, so the next step can build on it. And it lets you ask for things in plain language, instead of remembering exact flags and arguments. For a service where skipping end-to-end tests turns into production incidents, that is a small investment with a quick payoff.&lt;/p&gt;
&lt;h3&gt;Future Improvements&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Seed data is fixed-shape today.&lt;/strong&gt; The skill creates a small canned set of orgs, users, and projects. I would like to be able to describe the shape I need in plain English, something like “an org with twenty members, three projects, and a mix of viewer, editor, and owner roles”, and have the skill build it out, instead of writing each step by hand every time I need a new variation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The interaction is too plain-text right now.&lt;/strong&gt; When the skill needs an input from the user (Docker or local, which port to use, which user to log in as), it asks in regular chat text and waits for a typed reply. I would like those moments to show up as proper selectable choices inside Claude Code, the way Claude’s own prompts do, so the user can just pick an option instead of reading a paragraph and typing the answer back.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;No browser-side testing yet.&lt;/strong&gt; The current plugin understands Frontier’s proto layer and can call any RPC on demand, but Frontier also ships an Admin UI and a JavaScript SDK with a demo app. Anyone working on the JS SDK still has to test their changes by clicking through the UI by hand. I want to extend the plugin so it can drive the browser as well, simulating logins, button clicks, and form submissions, so SDK changes can be tested as easily as RPC changes already are.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;If your service takes more than a few minutes to bring up locally, the highest-leverage version of this is probably the one you do not write a doc for. Instead, you write the executable equivalent of the doc and let your team invoke it. Your first version will be rough, but the time it saves your team is real, and you get that time back very quickly.&lt;/p&gt;
&lt;p&gt;The plugin lives at &lt;a href=&quot;https://github.com/whoAbhishekSah/claude-marketplace&quot;&gt;whoAbhishekSah/claude-marketplace&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Managing Infrastructure at Scale With Terraform]]></title><description><![CDATA[Patterns for managing large-scale infrastructure with Terraform — modules, state, and the workflows that keep it sane.]]></description><link>https://absh.dev/terraform-transformation/</link><guid isPermaLink="false">https://absh.dev/terraform-transformation/</guid><pubDate>Fri, 13 Mar 2026 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;What this post covers:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Why &lt;code class=&quot;language-text&quot;&gt;for_each&lt;/code&gt; exists and how it avoids the index-shifting problem that &lt;code class=&quot;language-text&quot;&gt;count&lt;/code&gt; has&lt;/li&gt;
&lt;li&gt;How sets, maps, and maps of objects work with &lt;code class=&quot;language-text&quot;&gt;for_each&lt;/code&gt;, and why lists need &lt;code class=&quot;language-text&quot;&gt;toset()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;The flatten-then-map pattern for turning nested data into flat maps&lt;/li&gt;
&lt;li&gt;Dynamic blocks, the known-values constraint, and other gotchas&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;When your Terraform codebase manages a handful of resources, almost any approach works. But as infrastructure grows — more services, more environments, more accounts — you start needing ten, fifty, or a hundred copies of the same resource with slightly different config. At that point, how you loop over resources decides whether adding a new service takes one line or a hundred, and whether removing one accidentally destroys something else.&lt;/p&gt;
&lt;p&gt;Terraform gives you two ways to create multiple resources: &lt;code class=&quot;language-text&quot;&gt;count&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;for_each&lt;/code&gt;. This post is about &lt;code class=&quot;language-text&quot;&gt;for_each&lt;/code&gt; — how it works, where it helps at scale, and where it gets tricky.&lt;/p&gt;
&lt;h3&gt;Why Not &lt;code class=&quot;language-text&quot;&gt;count&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;count&lt;/code&gt; argument identifies resources by their position in a list:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;variable&lt;span class=&quot;token type variable&quot;&gt; &quot;bucket_names&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;logs&quot;&lt;/span&gt;, &lt;span class=&quot;token string&quot;&gt;&quot;data&quot;&lt;/span&gt;, &lt;span class=&quot;token string&quot;&gt;&quot;backups&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;aws_s3_bucket&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;this&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;count&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; length(var.bucket_names)
  &lt;span class=&quot;token property&quot;&gt;bucket&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;mycompany-&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;bucket_names&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token type variable&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In state, these resources are tracked as &lt;code class=&quot;language-text&quot;&gt;aws_s3_bucket.this[0]&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;aws_s3_bucket.this[1]&lt;/code&gt;, and &lt;code class=&quot;language-text&quot;&gt;aws_s3_bucket.this[2]&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you remove “data” from the middle of the list, “backups” shifts from index 2 to index 1. Terraform sees that index 1 has changed and that index 2 has disappeared — so it plans a destroy and recreate of the backups bucket. In production, that could mean deleting a bucket that holds customer data.&lt;/p&gt;
&lt;h3&gt;How &lt;code class=&quot;language-text&quot;&gt;for_each&lt;/code&gt; fixes this&lt;/h3&gt;
&lt;p&gt;Instead of tracking resources by position, &lt;code class=&quot;language-text&quot;&gt;for_each&lt;/code&gt; identifies each one by a &lt;strong&gt;name&lt;/strong&gt;:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;aws_s3_bucket&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;this&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;for_each&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; toset(&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;logs&quot;&lt;/span&gt;, &lt;span class=&quot;token string&quot;&gt;&quot;data&quot;&lt;/span&gt;, &lt;span class=&quot;token string&quot;&gt;&quot;backups&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;)
  &lt;span class=&quot;token property&quot;&gt;bucket&lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;mycompany-&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;each&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In state, these become &lt;code class=&quot;language-text&quot;&gt;aws_s3_bucket.this[&quot;logs&quot;]&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;aws_s3_bucket.this[&quot;data&quot;]&lt;/code&gt;, and &lt;code class=&quot;language-text&quot;&gt;aws_s3_bucket.this[&quot;backups&quot;]&lt;/code&gt;. If you remove “data”, only that resource gets destroyed, and the other two stay exactly as they are.&lt;/p&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;for_each&lt;/code&gt; argument accepts either a &lt;strong&gt;set&lt;/strong&gt; or a &lt;strong&gt;map&lt;/strong&gt;, but it does not accept a list directly. You can convert a list with &lt;code class=&quot;language-text&quot;&gt;toset()&lt;/code&gt;. When you use a set, &lt;code class=&quot;language-text&quot;&gt;each.key&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;each.value&lt;/code&gt; refer to the same thing. When you use a map, &lt;code class=&quot;language-text&quot;&gt;each.key&lt;/code&gt; gives you the key and &lt;code class=&quot;language-text&quot;&gt;each.value&lt;/code&gt; gives you the value.&lt;/p&gt;
&lt;h3&gt;Maps&lt;/h3&gt;
&lt;p&gt;Maps are where &lt;code class=&quot;language-text&quot;&gt;for_each&lt;/code&gt; gets more useful, because you can attach different configuration to each item:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;variable&lt;span class=&quot;token type variable&quot;&gt; &quot;buckets&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;logs&quot;&lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;us-east-1&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;data&quot;&lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;us-west-2&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;backups&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;eu-west-1&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;aws_s3_bucket&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;this&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;for_each&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.buckets
  &lt;span class=&quot;token property&quot;&gt;bucket&lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;mycompany-&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;each&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;tags&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;Region&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; each.value
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To add a bucket, you add one line to the map. To remove one, you delete a line. Terraform figures out the rest.&lt;/p&gt;
&lt;h3&gt;Maps of Objects&lt;/h3&gt;
&lt;p&gt;In most cases, each resource needs more than a single value. You can handle that with a map of objects:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;variable&lt;span class=&quot;token type variable&quot;&gt; &quot;services&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; map(object(&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;port&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; number
    &lt;span class=&quot;token property&quot;&gt;replicas&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; number
    &lt;span class=&quot;token property&quot;&gt;memory&lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; optional(string, &lt;span class=&quot;token string&quot;&gt;&quot;256Mi&quot;&lt;/span&gt;)
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;))

  &lt;span class=&quot;token property&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;api&quot;&lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;port&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt;, &lt;span class=&quot;token property&quot;&gt;replicas&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;, &lt;span class=&quot;token property&quot;&gt;memory&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;512Mi&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;worker&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;port&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;9090&lt;/span&gt;, &lt;span class=&quot;token property&quot;&gt;replicas&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;kubernetes_deployment&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;this&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;for_each&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.services

  &lt;span class=&quot;token keyword&quot;&gt;metadata&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; each.key
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;spec&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;replicas&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; each.value.replicas

    &lt;span class=&quot;token keyword&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;spec&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;container&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; each.key
          &lt;span class=&quot;token property&quot;&gt;image&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;myregistry/&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;each&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;:latest&quot;&lt;/span&gt;
          &lt;span class=&quot;token property&quot;&gt;port&lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; each.value.port
          &lt;span class=&quot;token property&quot;&gt;memory&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; each.value.memory
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Caveat:&lt;/strong&gt; Every object in the map must have the same shape. If “api” has a &lt;code class=&quot;language-text&quot;&gt;port&lt;/code&gt; field, then “worker” needs one too. You can use &lt;code class=&quot;language-text&quot;&gt;optional()&lt;/code&gt; with a default value for fields that not every entry needs.&lt;/p&gt;
&lt;h3&gt;Referencing &lt;code class=&quot;language-text&quot;&gt;for_each&lt;/code&gt; Resources&lt;/h3&gt;
&lt;p&gt;To reference a specific instance, you use the key in square brackets:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;output&lt;span class=&quot;token type variable&quot;&gt; &quot;api_name&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; kubernetes_deployment.this&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;api&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;.metadata&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;.name
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To reference all instances at once, you use a &lt;code class=&quot;language-text&quot;&gt;for&lt;/code&gt; expression to build a new map:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;output&lt;span class=&quot;token type variable&quot;&gt; &quot;all_names&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; for k, v in kubernetes_deployment.this : &lt;span class=&quot;token property&quot;&gt;k&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&gt; v.metadata&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;.name &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Caveat:&lt;/strong&gt; You cannot pass &lt;code class=&quot;language-text&quot;&gt;kubernetes_deployment.this&lt;/code&gt; directly as an output. Terraform expects you to transform it with a &lt;code class=&quot;language-text&quot;&gt;for&lt;/code&gt; expression. The splat syntax (&lt;code class=&quot;language-text&quot;&gt;[*]&lt;/code&gt;) that works with &lt;code class=&quot;language-text&quot;&gt;count&lt;/code&gt; does not apply to &lt;code class=&quot;language-text&quot;&gt;for_each&lt;/code&gt; resources.&lt;/p&gt;
&lt;h3&gt;“Known Values” Constraint&lt;/h3&gt;
&lt;p&gt;The keys you pass to &lt;code class=&quot;language-text&quot;&gt;for_each&lt;/code&gt; must be known at &lt;strong&gt;plan time&lt;/strong&gt;, which means they cannot come from the output of another resource. For example, this fails:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;aws_route_table_association&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;this&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;for_each&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; for s in aws_subnet.this : &lt;span class=&quot;token property&quot;&gt;s.id&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&gt; s &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;subnet_id&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; each.key
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The subnet IDs do not exist until Terraform actually creates them, so it cannot determine the keys during the plan phase. The fix is to drive both resources from the same input variable:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;variable&lt;span class=&quot;token type variable&quot;&gt; &quot;subnet_config&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;subnet-a&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;az&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;us-east-1a&quot;&lt;/span&gt;, &lt;span class=&quot;token property&quot;&gt;cidr&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;10.0.1.0/24&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;subnet-b&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;az&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;us-east-1b&quot;&lt;/span&gt;, &lt;span class=&quot;token property&quot;&gt;cidr&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;10.0.2.0/24&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;aws_subnet&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;this&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;for_each&lt;/span&gt;          &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.subnet_config
  &lt;span class=&quot;token property&quot;&gt;availability_zone&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; each.value.az
  &lt;span class=&quot;token property&quot;&gt;cidr_block&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; each.value.cidr
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;aws_route_table_association&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;this&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;for_each&lt;/span&gt;       &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.subnet_config
  &lt;span class=&quot;token property&quot;&gt;subnet_id&lt;/span&gt;      &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; aws_subnet.this&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;each.key&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;.id
  &lt;span class=&quot;token property&quot;&gt;route_table_id&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; aws_route_table.main.id
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Since both resources share the same map, the keys come from a variable and are known at plan time. This is worth doing on purpose — when one map drives multiple resources, the code becomes easier to follow and less likely to break.&lt;/p&gt;
&lt;h3&gt;Flattening Nested Data&lt;/h3&gt;
&lt;p&gt;When your data is nested, you need to turn it into a flat map before &lt;code class=&quot;language-text&quot;&gt;for_each&lt;/code&gt; can work with it. For example, say you have multiple users and each of them needs several IAM policy attachments:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;variable&lt;span class=&quot;token type variable&quot;&gt; &quot;user_policies&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;alice&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;arn:aws:iam::policy/ReadOnly&quot;&lt;/span&gt;, &lt;span class=&quot;token string&quot;&gt;&quot;arn:aws:iam::policy/S3Full&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;bob&quot;&lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;arn:aws:iam::policy/ReadOnly&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The transformation happens in three steps. First, nested &lt;code class=&quot;language-text&quot;&gt;for&lt;/code&gt; loops produce a list of lists. Then, &lt;code class=&quot;language-text&quot;&gt;flatten&lt;/code&gt; collapses them into a single flat list. Finally, a &lt;code class=&quot;language-text&quot;&gt;for&lt;/code&gt; expression converts that list into a map with composite keys:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;locals&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;user_policy_pairs&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; flatten(&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    for user, policies in var.user_policies : &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      for policy in policies : &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;user&lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; user
        &lt;span class=&quot;token property&quot;&gt;policy&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; policy
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;)

  &lt;span class=&quot;token property&quot;&gt;user_policy_map&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    for pair in local.user_policy_pairs :
    &lt;span class=&quot;token property&quot;&gt;&quot;${pair.user}-${basename(pair.policy)}&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&gt; pair
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;aws_iam_user_policy_attachment&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;this&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;for_each&lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; local.user_policy_map
  &lt;span class=&quot;token property&quot;&gt;user&lt;/span&gt;       &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; each.value.user
  &lt;span class=&quot;token property&quot;&gt;policy_arn&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; each.value.policy
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Caveat:&lt;/strong&gt; The composite key must be unique across all entries. If you change the key format later, Terraform will treat every affected resource as a destroy-and-create.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Caveat:&lt;/strong&gt; Make sure you carry the outer key (like the user name) into the flattened object. If you drop it during flattening, you will not be able to reference it in the resource block later.&lt;/p&gt;
&lt;h3&gt;Dynamic Blocks Inside &lt;code class=&quot;language-text&quot;&gt;for_each&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;There are situations where you need to loop across resources and also loop within a single resource. The outer &lt;code class=&quot;language-text&quot;&gt;for_each&lt;/code&gt; handles the first part, and a &lt;code class=&quot;language-text&quot;&gt;dynamic&lt;/code&gt; block handles the second:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;variable&lt;span class=&quot;token type variable&quot;&gt; &quot;security_groups&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;web&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;ingress_rules&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;port&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;, &lt;span class=&quot;token property&quot;&gt;cidr&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;0.0.0.0/0&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;,
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;port&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;443&lt;/span&gt;, &lt;span class=&quot;token property&quot;&gt;cidr&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;0.0.0.0/0&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;,
      &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;api&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;ingress_rules&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;port&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt;, &lt;span class=&quot;token property&quot;&gt;cidr&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;10.0.0.0/8&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;,
      &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;aws_security_group&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;this&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;for_each&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.security_groups
  &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; each.key

  dynamic &lt;span class=&quot;token string&quot;&gt;&quot;ingress&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;for_each&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; each.value.ingress_rules
    &lt;span class=&quot;token keyword&quot;&gt;content&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;from_port&lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; ingress.value.port
      &lt;span class=&quot;token property&quot;&gt;to_port&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; ingress.value.port
      &lt;span class=&quot;token property&quot;&gt;protocol&lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;tcp&quot;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;cidr_blocks&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;ingress.value.cidr&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Caveat:&lt;/strong&gt; Inside the &lt;code class=&quot;language-text&quot;&gt;dynamic&lt;/code&gt; block, the iterator is called &lt;code class=&quot;language-text&quot;&gt;ingress.value&lt;/code&gt; rather than &lt;code class=&quot;language-text&quot;&gt;each.value&lt;/code&gt;. It takes its name from the block label. The &lt;code class=&quot;language-text&quot;&gt;each&lt;/code&gt; variable still refers to the outer &lt;code class=&quot;language-text&quot;&gt;for_each&lt;/code&gt; on the resource itself.&lt;/p&gt;
&lt;h3&gt;Putting It Together: Multi-Environment EKS Node Groups&lt;/h3&gt;
&lt;p&gt;This pattern is close to what you would see in a real codebase where a team runs Kubernetes across staging and production. A single variable drives all the node groups:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;variable&lt;span class=&quot;token type variable&quot;&gt; &quot;environments&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; map(object(&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;cluster_name&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; string
    &lt;span class=&quot;token property&quot;&gt;node_groups&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; map(object(&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;instance_type&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; string
      &lt;span class=&quot;token property&quot;&gt;min_size&lt;/span&gt;      &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; number
      &lt;span class=&quot;token property&quot;&gt;max_size&lt;/span&gt;      &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; number
      &lt;span class=&quot;token property&quot;&gt;desired_size&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; number
      &lt;span class=&quot;token property&quot;&gt;disk_size&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; number
      &lt;span class=&quot;token property&quot;&gt;labels&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; map(string)
      &lt;span class=&quot;token property&quot;&gt;taints&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; list(object(&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;key&lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; string
        &lt;span class=&quot;token property&quot;&gt;value&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; string
        &lt;span class=&quot;token property&quot;&gt;effect&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; string
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;))
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;))
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;))
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;locals&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;all_node_groups&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; flatten(&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    for env_name, env in var.environments : &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      for ng_name, ng in env.node_groups : &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;env_name&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; env_name
        &lt;span class=&quot;token property&quot;&gt;cluster_name&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; env.cluster_name
        &lt;span class=&quot;token property&quot;&gt;ng_name&lt;/span&gt;      &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; ng_name
        &lt;span class=&quot;token property&quot;&gt;ng_config&lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; ng
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;)

  &lt;span class=&quot;token property&quot;&gt;node_group_map&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    for ng in local.all_node_groups :
    &lt;span class=&quot;token property&quot;&gt;&quot;${ng.env_name}-${ng.ng_name}&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&gt; ng
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;aws_eks_node_group&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;this&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;for_each&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; local.node_group_map
  &lt;span class=&quot;token property&quot;&gt;cluster_name&lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; each.value.cluster_name
  &lt;span class=&quot;token property&quot;&gt;node_group_name&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; each.value.ng_name
  &lt;span class=&quot;token property&quot;&gt;instance_types&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;each.value.ng_config.instance_type&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;disk_size&lt;/span&gt;       &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; each.value.ng_config.disk_size

  &lt;span class=&quot;token keyword&quot;&gt;scaling_config&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;min_size&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; each.value.ng_config.min_size
    &lt;span class=&quot;token property&quot;&gt;max_size&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; each.value.ng_config.max_size
    &lt;span class=&quot;token property&quot;&gt;desired_size&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; each.value.ng_config.desired_size
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;labels&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; merge(each.value.ng_config.labels, &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;environment&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; each.value.env_name
    &lt;span class=&quot;token property&quot;&gt;node_group&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; each.value.ng_name
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;)

  dynamic &lt;span class=&quot;token string&quot;&gt;&quot;taint&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;for_each&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; each.value.ng_config.taints
    &lt;span class=&quot;token keyword&quot;&gt;content&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;key&lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; taint.value.key
      &lt;span class=&quot;token property&quot;&gt;value&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; taint.value.value
      &lt;span class=&quot;token property&quot;&gt;effect&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; taint.value.effect
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To add an environment or a node group, you add an entry to the map. The flatten-then-map step produces keys like “production-observability” and “staging-general”, and Terraform manages each one independently in state.&lt;/p&gt;
&lt;h3&gt;Summary&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;&lt;code class=&quot;language-text&quot;&gt;count&lt;/code&gt; vs &lt;code class=&quot;language-text&quot;&gt;for_each&lt;/code&gt;:&lt;/strong&gt; You should use &lt;code class=&quot;language-text&quot;&gt;count&lt;/code&gt; only for conditional creation (&lt;code class=&quot;language-text&quot;&gt;count = var.enabled ? 1 : 0&lt;/code&gt;), and reach for &lt;code class=&quot;language-text&quot;&gt;for_each&lt;/code&gt; whenever you need multiple instances.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Input types:&lt;/strong&gt; The &lt;code class=&quot;language-text&quot;&gt;for_each&lt;/code&gt; argument takes a set or a map. If you have a list, you can convert it with &lt;code class=&quot;language-text&quot;&gt;toset()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Known values:&lt;/strong&gt; All keys must be known at plan time, so you should drive them from variables rather than resource outputs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Flattening:&lt;/strong&gt; Nested data needs to be flattened into a map with composite keys, and you should carry context from the outer loops into the flattened objects so it is available in the resource block.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;State keys are identity:&lt;/strong&gt; If you change a key, Terraform will destroy and recreate that resource. Choose your keys carefully, and use &lt;code class=&quot;language-text&quot;&gt;terraform state mv&lt;/code&gt; when you need to rename one.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Dynamic blocks:&lt;/strong&gt; The iterator inside a &lt;code class=&quot;language-text&quot;&gt;dynamic&lt;/code&gt; block is named after the block label, not &lt;code class=&quot;language-text&quot;&gt;each&lt;/code&gt;, so keep track of which variable refers to which loop.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Go Contexts: What I Learned by Getting Things Wrong]]></title><description><![CDATA[The subtle parts of Go's context package — cancellation, deadlines and propagation — learned by debugging things that broke in production.]]></description><link>https://absh.dev/go-context/</link><guid isPermaLink="false">https://absh.dev/go-context/</guid><pubDate>Sun, 01 Mar 2026 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;If you write Go and work on distributed systems, you will use &lt;code class=&quot;language-text&quot;&gt;context&lt;/code&gt; in almost every function you write. It shows up in HTTP handlers, database calls, gRPC methods, and Kubernetes controllers. Most of us learn the basics early — pass a context, check for cancellation, move on. But the subtle parts? Those show up when things break in production and you spend a Thursday night staring at goroutine dumps.&lt;/p&gt;
&lt;p&gt;I recently spent time studying Go contexts from the ground up. Not the documentation kind of study, but the kind where you write code, make mistakes, and then figure out why things went wrong. This post captures what I learned, including the parts that surprised me.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;1. Cancelling Work from the Outside&lt;/h2&gt;
&lt;p&gt;The simplest use of context is cancellation. You have a function doing work, and you want the caller to be able to say “stop.” The function itself does not decide when to stop. The caller does.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;go&quot;&gt;&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; cancel &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WithCancelCause&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This gives you a context and a cancel function. When you call &lt;code class=&quot;language-text&quot;&gt;cancel(someError)&lt;/code&gt;, the context’s &lt;code class=&quot;language-text&quot;&gt;Done()&lt;/code&gt; channel closes, and anything watching it knows to wrap up. The error you pass in is the &lt;strong&gt;cause&lt;/strong&gt;, and you can retrieve it later with &lt;code class=&quot;language-text&quot;&gt;context.Cause(ctx)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here is a small worker I built while learning. It wraps a function and runs it in a loop, with support for cancellation and cleanup callbacks:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;go&quot;&gt;&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; Worker &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    fn         &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;error&lt;/span&gt;
    ctx        context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Context
    cancel     &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cause &lt;span class=&quot;token builtin&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    afterFuncs &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;NewWorker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fn &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;Worker &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;Worker&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;fn&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; fn&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;w &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;Worker&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; w&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ctx &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    w&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; w&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cancel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WithCancelCause&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; fn &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;range&lt;/span&gt; w&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;afterFuncs &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;AfterFunc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;w&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; fn&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;go&lt;/span&gt; w&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;work&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;w &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;Worker&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;AfterStop&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fn &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; w&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ctx &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    w&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;afterFuncs &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;w&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;afterFuncs&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; fn&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;w &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;Worker&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Stop&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; w&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ctx &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    w&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cancel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ErrManual&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;w &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;Worker&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Err&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Cause&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;w&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The idea is simple. The caller creates a worker with a function, registers any cleanup callbacks with &lt;code class=&quot;language-text&quot;&gt;AfterStop&lt;/code&gt;, and then calls &lt;code class=&quot;language-text&quot;&gt;Start&lt;/code&gt;. The worker creates its own context and cancel function, registers the cleanup callbacks using &lt;code class=&quot;language-text&quot;&gt;context.AfterFunc&lt;/code&gt;, and launches the work loop in a goroutine. When someone calls &lt;code class=&quot;language-text&quot;&gt;Stop&lt;/code&gt;, it cancels the context with &lt;code class=&quot;language-text&quot;&gt;ErrManual&lt;/code&gt;. If the function itself fails, the loop cancels the context with &lt;code class=&quot;language-text&quot;&gt;ErrFailed&lt;/code&gt;. Either way, the registered &lt;code class=&quot;language-text&quot;&gt;AfterFunc&lt;/code&gt; callbacks run after cancellation.&lt;/p&gt;
&lt;p&gt;The work loop looks like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;go&quot;&gt;&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;w &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;Worker&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;work&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt;w&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Done&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            err &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; w&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                w&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cancel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ErrFailed&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The worker checks &lt;code class=&quot;language-text&quot;&gt;ctx.Done()&lt;/code&gt; at the top of each loop. If the context is cancelled, it returns. Otherwise, it runs the function.&lt;/p&gt;
&lt;p&gt;This works, but there is a catch.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Catch: The first cancel wins&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you call &lt;code class=&quot;language-text&quot;&gt;cancel&lt;/code&gt; more than once with different causes, only the first one counts. The rest are silently ignored.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;go&quot;&gt;&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; cancel &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WithCancelCause&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;cancel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;errors&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;New&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;first&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;cancel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;errors&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;New&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;second&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
fmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Cause&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// prints: first&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This matters when you have multiple reasons a piece of work might stop. Say your worker fails internally with &lt;code class=&quot;language-text&quot;&gt;ErrFailed&lt;/code&gt;, and then someone also calls &lt;code class=&quot;language-text&quot;&gt;Stop()&lt;/code&gt; with &lt;code class=&quot;language-text&quot;&gt;ErrManual&lt;/code&gt;. The cause will be &lt;code class=&quot;language-text&quot;&gt;ErrFailed&lt;/code&gt;, because that happened first. If you are debugging a failure and checking the cause, you need to know that the cause reflects whoever got there first, not whoever you expected.&lt;/p&gt;
&lt;h2&gt;2. Cancellation Is Cooperative, Not Preemptive&lt;/h2&gt;
&lt;p&gt;Cancellation in Go is often misunderstood as something that happens automatically. It does not. Go has no mechanism to kill a goroutine from the outside. When you cancel a context, all you are doing is closing a channel. If the code running inside the goroutine never checks that channel, it will keep running as if nothing happened.&lt;/p&gt;
&lt;p&gt;Lets take a look at this worker loop again:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;go&quot;&gt;&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt;w&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Done&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    err &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; w&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If &lt;code class=&quot;language-text&quot;&gt;w.fn()&lt;/code&gt; takes 30 seconds to run, and you cancel the context at second 2, the goroutine does not exit at second 2. It exits after 30 seconds, when &lt;code class=&quot;language-text&quot;&gt;fn&lt;/code&gt; returns and the loop gets back to the &lt;code class=&quot;language-text&quot;&gt;select&lt;/code&gt; statement. The cancellation was sitting there in the &lt;code class=&quot;language-text&quot;&gt;Done()&lt;/code&gt; channel the whole time. Nobody was listening.&lt;/p&gt;
&lt;p&gt;This is a fundamental design decision in Go. The language gives you the tools to signal cancellation, but it is your responsibility to check for it. If your function does not look at the context, it will not respond to cancellation.&lt;/p&gt;
&lt;h3&gt;The fix: pass the context down&lt;/h3&gt;
&lt;p&gt;If your function does something slow — an HTTP call, a database query, a file transfer — it should accept a &lt;code class=&quot;language-text&quot;&gt;context.Context&lt;/code&gt; and pass it to whatever it calls.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;go&quot;&gt;&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Instead of this:&lt;/span&gt;
fn &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;error&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Do this:&lt;/span&gt;
fn &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Context&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;error&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now, inside &lt;code class=&quot;language-text&quot;&gt;fn&lt;/code&gt;, if you are making an HTTP request, you use &lt;code class=&quot;language-text&quot;&gt;http.NewRequestWithContext(ctx, ...)&lt;/code&gt;. If the context is cancelled, the HTTP client aborts the request. The cancellation propagates through the entire call chain, from the top-level caller all the way down to the network socket. That is the whole point of &lt;code class=&quot;language-text&quot;&gt;context&lt;/code&gt; — it flows downward through your program.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Catch: Do not spawn goroutines just to wrap cancellation&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;You might think: “I will launch &lt;code class=&quot;language-text&quot;&gt;fn&lt;/code&gt; in a goroutine, and if the context is cancelled, I will just abandon it.” The problem is that the goroutine is still running. Nobody is waiting for its result. It is leaked. You traded one problem (slow cancellation) for another (resource leaks). The right approach is always to make the function itself aware of the context.&lt;/p&gt;
&lt;h2&gt;3. Sequential Operations and Context in Kubernetes Operators&lt;/h2&gt;
&lt;p&gt;If you have written a Kubernetes operator, you have seen this pattern. The controller-runtime framework calls your &lt;code class=&quot;language-text&quot;&gt;Reconcile&lt;/code&gt; function whenever a custom resource changes. It passes you a context. That context gets cancelled if the operator pod is shutting down — during a rolling deploy, a node drain, or a leader election change.&lt;/p&gt;
&lt;p&gt;Say your reconciler does three things in sequence:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;go&quot;&gt;&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;r &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;Reconciler&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Reconcile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Context&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; req ctrl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctrl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Result&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    meta&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; r&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fetchMetadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; ctrl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Result&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    params&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; r&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;validateParams&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; meta&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; ctrl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Result&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    err &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; r&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pushConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; params&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; ctrl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Result&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; ctrl&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Result&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Each call takes 2 to 5 seconds. Now your operator pod gets a SIGTERM during a rolling update. You are halfway through &lt;code class=&quot;language-text&quot;&gt;validateParams&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;What happens is straightforward once you understand cooperative cancellation. The context gets cancelled by the framework. But &lt;code class=&quot;language-text&quot;&gt;validateParams&lt;/code&gt; is already running. It does not stop mid-execution just because the context flipped. It finishes, returns an error (assuming it checks the context internally), and then the error check prevents &lt;code class=&quot;language-text&quot;&gt;pushConfig&lt;/code&gt; from running. If &lt;code class=&quot;language-text&quot;&gt;validateParams&lt;/code&gt; does not check the context, it runs to completion, returns a result, and then &lt;code class=&quot;language-text&quot;&gt;pushConfig&lt;/code&gt; gets called with a cancelled context.&lt;/p&gt;
&lt;p&gt;The important thing here is that &lt;strong&gt;you must check errors between each call&lt;/strong&gt;. If you ignore the error from &lt;code class=&quot;language-text&quot;&gt;fetchMetadata&lt;/code&gt; and proceed to &lt;code class=&quot;language-text&quot;&gt;validateParams&lt;/code&gt;, you are doing work with potentially invalid data on a cancelled context. The context cancellation only helps if two things are true: the function you called respects the context, and you check the error it returns.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Catch: Partial completion and idempotency&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;What if metadata was fetched and validation passed, but &lt;code class=&quot;language-text&quot;&gt;pushConfig&lt;/code&gt; failed because the context was cancelled? The next reconcile starts from scratch. Is that a problem?&lt;/p&gt;
&lt;p&gt;In most operators, no. The standard approach is to make reconciliation idempotent. You store progress in the resource’s status subresource. Each reconcile checks current state against desired state and does only what is needed. If metadata is already stored in status, the next reconcile skips that step. The status acts as your checkpoint.&lt;/p&gt;
&lt;p&gt;This is why Kubernetes operators lean on status subresources so heavily. They are not just for reporting — they are the mechanism that makes retries cheap and safe.&lt;/p&gt;
&lt;h2&gt;4. Parent-Child Context Chains and Cause Propagation&lt;/h2&gt;
&lt;p&gt;Contexts form a tree. You start with &lt;code class=&quot;language-text&quot;&gt;context.Background()&lt;/code&gt;, then create children from it, and children from those children. When a parent is cancelled, all of its children are cancelled too.&lt;/p&gt;
&lt;p&gt;But here is the part that caught me off guard: the cause propagates from parent to child.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;go&quot;&gt;&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; cancel &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WithTimeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Second&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;cancel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

childCtx&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; childCancel &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WithCancelCause&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;childCancel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;errors&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;New&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;cleanup&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When the 5-second timeout fires, the parent is cancelled with &lt;code class=&quot;language-text&quot;&gt;context.DeadlineExceeded&lt;/code&gt;. The child gets cancelled too, because its parent is done. Now, what does &lt;code class=&quot;language-text&quot;&gt;context.Cause(childCtx)&lt;/code&gt; return?&lt;/p&gt;
&lt;p&gt;I expected it to return &lt;code class=&quot;language-text&quot;&gt;&quot;cleanup&quot;&lt;/code&gt;, because that is what I passed to &lt;code class=&quot;language-text&quot;&gt;childCancel&lt;/code&gt;. But it returns &lt;code class=&quot;language-text&quot;&gt;context.DeadlineExceeded&lt;/code&gt;. The parent’s timeout cancelled the child before &lt;code class=&quot;language-text&quot;&gt;childCancel&lt;/code&gt; was ever called. And since the first cancel wins, the deferred &lt;code class=&quot;language-text&quot;&gt;childCancel(errors.New(&quot;cleanup&quot;))&lt;/code&gt; is a no-op — the cause was already set by the parent.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Catch: A child’s cancel function only matters if the child is cancelled before its parent&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you are relying on &lt;code class=&quot;language-text&quot;&gt;context.Cause&lt;/code&gt; to distinguish between “we timed out” and “we were explicitly stopped,” you need to make sure the explicit stop happens first. Otherwise, the parent’s cause will already be set.&lt;/p&gt;
&lt;h2&gt;5. &lt;code class=&quot;language-text&quot;&gt;context.Background()&lt;/code&gt; Is the Root&lt;/h2&gt;
&lt;p&gt;A quick note that seems obvious but is worth stating: &lt;code class=&quot;language-text&quot;&gt;context.Background()&lt;/code&gt; can never be cancelled. It has no deadline, no cancel function, and no values. It is the starting point of every context tree. Same goes for &lt;code class=&quot;language-text&quot;&gt;context.TODO()&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;go&quot;&gt;&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;ctx &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
fmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Err&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;          &lt;span class=&quot;token comment&quot;&gt;// &amp;lt;nil&gt;&lt;/span&gt;
fmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Cause&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &amp;lt;nil&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You create all other contexts from this root. Nothing cancels it.&lt;/p&gt;
&lt;h2&gt;6. Fire-and-Forget Goroutines and Context Lifetimes&lt;/h2&gt;
&lt;p&gt;This one shows up in almost every HTTP service I have seen. You have a handler that does some work and then kicks off a background task:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;go&quot;&gt;&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;w http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ResponseWriter&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; r &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    data&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetchFromDB&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;r&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; query&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;go&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sendToKafka&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;r&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// fire and forget&lt;/span&gt;

    w&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Write&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;response&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The bug: when the handler returns, the request context is cancelled. Your Kafka goroutine is using that same context. The Kafka write races against context cancellation, and most of the time, it loses. Your audit log silently drops messages, and nobody notices until someone asks “why are there gaps in the logs?”&lt;/p&gt;
&lt;p&gt;The fix is to use a different context for work that must outlive the request:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;go&quot;&gt;&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;bgCtx&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; bgCancel &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WithTimeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Second&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;go&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;bgCancel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;sendToKafka&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;bgCtx&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This context is not tied to the request. It has its own timeout, and it will not be cancelled when the handler returns.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Catch: Request contexts die when the handler returns&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Any goroutine that needs to outlive the HTTP request must not use the request’s context. Create a new one from &lt;code class=&quot;language-text&quot;&gt;context.Background()&lt;/code&gt; with its own timeout.&lt;/p&gt;
&lt;h2&gt;7. Graceful Shutdown: Structuring the Context Hierarchy&lt;/h2&gt;
&lt;p&gt;When your service receives a SIGTERM — during a deploy, a node drain, or a scaling event — you need every subsystem to stop cleanly. Contexts give you the structure for this.&lt;/p&gt;
&lt;p&gt;Say your service has three subsystems: an HTTP server, a Kafka consumer, and a background reconciler. Here is how you wire them up:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;go&quot;&gt;&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    parentCtx&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; cancel &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WithCancelCause&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    sigCh &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;make&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;chan&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Signal&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    signal&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Notify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sigCh&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; syscall&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SIGTERM&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; syscall&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SIGINT&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;go&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        sig &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt;sigCh
        &lt;span class=&quot;token function&quot;&gt;cancel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Errorf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;received signal: %v&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sig&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    g&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; gCtx &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; errgroup&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WithContext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;parentCtx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    g&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Go&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; httpServer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;gCtx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    g&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Go&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; kafkaConsumer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;gCtx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    g&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Go&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; reconciler&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;gCtx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; g&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Wait&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        log&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Printf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;shutdown: %v&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When SIGTERM arrives, the parent context is cancelled, which propagates to all three subsystems. The &lt;code class=&quot;language-text&quot;&gt;errgroup&lt;/code&gt; waits for all of them to finish. You do not exit the process until every subsystem has confirmed it is done.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Catch 1: Signal handling catches everything if you are not specific&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;signal.Notify(ch)&lt;/code&gt; with no signal arguments will catch every signal your process receives, including harmless ones like SIGCHLD and SIGURG. Always be explicit about which signals you care about.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Catch 2: Cleanup operations need a live context&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;When your Kafka consumer’s context is cancelled, it needs to commit the offset of the last message it processed. But if you use the cancelled context to commit, the commit will fail — the context is already done.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;go&quot;&gt;&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;k &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;KafkaConsumer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Context&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        msg&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; k&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;reader&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;FetchMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// respects cancellation&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; err
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        k&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;msg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        k&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;reader&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;CommitMessages&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; msg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// must succeed&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The fetch uses the cancellable context — that is how the consumer knows to stop pulling new messages. But the commit uses &lt;code class=&quot;language-text&quot;&gt;context.Background()&lt;/code&gt;, because the commit must go through regardless of whether the consumer is shutting down.&lt;/p&gt;
&lt;h2&gt;8. Fan-Out: Cancelling the Stragglers&lt;/h2&gt;
&lt;p&gt;Here is a pattern that shows up in API gateways and aggregation services. You call five downstream services in parallel. You need any three to succeed. Once you have three, cancel the remaining two.&lt;/p&gt;
&lt;p&gt;The natural instinct is to cancel the parent context. But that is wrong. The parent context is your request context — cancelling it would also cancel your ability to write the response. Instead, create a child context specifically for the fan-out:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;go&quot;&gt;&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;aggregate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;w http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ResponseWriter&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; r &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    parentCtx &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; r&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    fanoutCtx&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; cancelFanout &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WithCancel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;parentCtx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;cancelFanout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    results &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;make&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;chan&lt;/span&gt; Result&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// buffered to prevent goroutine leaks&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; svc &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;range&lt;/span&gt; services &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;go&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;s Service&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            val&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; s&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Call&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fanoutCtx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            results &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt; Result&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;val&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; val&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; err&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;svc&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; successes &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;Result
    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt;parentCtx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Done&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;respond&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;w&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; successes&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// return whatever we have&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; r &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt;results&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; r&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;err &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                successes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;successes&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; r&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;successes&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token function&quot;&gt;cancelFanout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// cancel remaining, NOT the parent&lt;/span&gt;
                    &lt;span class=&quot;token function&quot;&gt;respond&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;w&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; successes&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;respond&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;w&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; successes&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Catch 1: Buffer your channels&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The channel must be buffered with capacity equal to the number of goroutines. After you cancel and return, the remaining goroutines will eventually finish and try to write their results. If the channel is unbuffered, they block forever. That is a goroutine leak. A buffered channel lets them write and exit, even though nobody reads those results.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Catch 2: Do not cancel the parent to stop children&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Create a dedicated child context for the group of work you want to cancel. The parent context should stay alive for your own operations, like writing the HTTP response.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Contexts are a small API. Four functions, a couple of interfaces. But the patterns that emerge from them — cancellation chains, graceful shutdown, fan-out coordination — are the backbone of how Go services manage work. Getting them right is the difference between a service that shuts down cleanly and one that leaks goroutines, drops messages, and leaves you debugging at 2 AM.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Concurrency Controls in Golang]]></title><description><![CDATA[From simple data pipelines to token buckets — practical patterns for controlling concurrency in Go.]]></description><link>https://absh.dev/go-concurrency/</link><guid isPermaLink="false">https://absh.dev/go-concurrency/</guid><pubDate>Thu, 19 Feb 2026 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;From Simple Data Pipelines to Token Buckets&lt;/p&gt;
&lt;p&gt;In my day-to-day role as a Site Reliability Engineer, my primary focus naturally gravitates toward system stability, infrastructure scaling, and Kubernetes management. While I write a considerable amount of Go and Python to automate these operations, I rarely get the opportunity to build complex, highly concurrent backend patterns from scratch. Lately, I decided to look closer at how high-throughput systems actually process all that data, which naturally led me to study Go’s concurrency models. I wanted to step outside my usual operational boundaries and understand exactly how software engineers manage massive data streams efficiently without overwhelming their infrastructure.&lt;/p&gt;
&lt;p&gt;I’ve always found that the best way to actually understand a complex topic is to try explaining it. I’m writing this post to solidify my own grasp on these architectural patterns, breaking them down from a basic data pipeline all the way up to a production-grade token bucket rate limiter&lt;/p&gt;
&lt;h3&gt;Level 1: The Basic Pipeline for Stream Processing&lt;/h3&gt;
&lt;p&gt;Imagine you need to parse a massive 50GB log file to extract specific error messages. If you attempt to load that entire file into memory at once, your application will inevitably crash due to memory exhaustion. The elegant solution to this problem is the Pipeline Pattern, which allows you to break the workload into distinct processing stages that run simultaneously. By passing data downstream through Go channels, your application only ever needs to hold a single line of the file in memory at any given time.&lt;/p&gt;
&lt;p&gt;When designing a basic pipeline, the architecture generally flows through three primary components:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;The Reader (Submitter):&lt;/strong&gt; This stage reads the source file line-by-line and pushes each string into an outgoing channel.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The Transformer:&lt;/strong&gt; This middle stage reads from the incoming channel, performs the necessary filtering or modifications, and pushes the successful results to the next channel.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The Sinker (Consumer):&lt;/strong&gt; The final stage collects the processed results and handles the final output or database insertion.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;go&quot;&gt;&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;aggregateLog&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Context&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; filepath &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	pending &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;reader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; filepath&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; 
	collector &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;transformer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pending&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; 
	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sinker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;collector&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; 
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;⚠️ Common Pitfall: The Blocking Trap&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;When writing these individual stages, it is incredibly easy to accidentally block a goroutine forever, which creates a silent memory leak. If a user cancels the operation or a system timeout occurs, your goroutines must be able to recognize that signal and exit immediately.&lt;/p&gt;
&lt;p&gt;If you simply send data to a channel like &lt;code class=&quot;language-text&quot;&gt;pending &amp;lt;- line&lt;/code&gt;, and the downstream consumer has already stopped listening, your program will hang indefinitely. To prevent this, you should always wrap your channel sends and receives in a &lt;code class=&quot;language-text&quot;&gt;select&lt;/code&gt; statement alongside the context cancellation signal.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;go&quot;&gt;&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; pending &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt; line&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// The data was successfully sent downstream&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Done&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// The context was cancelled, so we shut down gracefully&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In a real-world backend, requests frequently get cancelled, connections drop, or API timeouts expire. The &lt;code class=&quot;language-text&quot;&gt;context&lt;/code&gt; is how Go signals your application to stop working on a task that is no longer needed. If your goroutines ignore this cancellation signal while waiting to send or receive data on a channel, they will hang in the background forever. Over time, these stranded goroutines accumulate and cause silent memory leaks that degrade performance and eventually crash your application.&lt;/p&gt;
&lt;h3&gt;Level 2: Scaling Up with Fan-Out and Fan-In&lt;/h3&gt;
&lt;p&gt;While a linear pipeline is fantastic for memory management, it introduces a new bottleneck if your middle transformation stage is computationally expensive. If your transformer performs a heavy regex match or a slow database lookup that takes a full second, your entire program operates at a maximum speed of one line per second.&lt;/p&gt;
&lt;p&gt;To resolve this bottleneck, we introduce the Fan-Out pattern. Instead of relying on a single transformer, we spawn a pool of worker goroutines that all read concurrently from the exact same input channel.&lt;/p&gt;
&lt;p&gt;Managing multiple workers safely requires a &lt;code class=&quot;language-text&quot;&gt;sync.WaitGroup&lt;/code&gt; to track their progress. The most challenging aspect of this pattern is knowing exactly when to close the output channel. If you close the channel prematurely, your active workers will panic when they try to send data. Conversely, if you forget to close it entirely, your final sinker stage will wait forever.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;go&quot;&gt;&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;transformDispatcher&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Context&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pending &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;chan&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;chan&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	collector &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;make&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;chan&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	wg &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;sync&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;WaitGroup&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
	&lt;span class=&quot;token comment&quot;&gt;// Fan-Out: Spawn multiple parallel workers&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		wg&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;go&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;worker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pending&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; collector&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; wg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    
	&lt;span class=&quot;token comment&quot;&gt;// Fan-In: Wait for all workers to finish in a background routine, then close&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;go&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		wg&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Wait&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;token function&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;collector&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    
	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; collector
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This design gives you some great built-in benefits: true parallelism, natural load balancing (since multiple goroutines are actively pulling from one shared channel), and an organized, ordered completion of tasks.&lt;/p&gt;
&lt;p&gt;However, there is one major caveat you must keep in mind: &lt;strong&gt;output order is not preserved&lt;/strong&gt;. Because the workers operate independently and process data at slightly different speeds, the results will arrive at the final sinker completely out of sequence. If your application strictly requires the output to match the original input order, this specific Fan-Out pattern is incorrect and you will need to reach for a different design.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;⚠️ Common Pitfall: The “Busy Wait” Loop&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;When you write the infinite loop for your worker routines, it is tempting to include a &lt;code class=&quot;language-text&quot;&gt;default&lt;/code&gt; case in your &lt;code class=&quot;language-text&quot;&gt;select&lt;/code&gt; block to handle moments when the channel is empty. You must avoid doing this.&lt;/p&gt;
&lt;p&gt;If you include a default case, you accidentally create a “busy wait.” Instead of pausing, the loop runs millions of times per second whenever there is no immediate data available, which will entirely consume your CPU resources.&lt;/p&gt;
&lt;p&gt;Here is what that mistake looks like in our log transformer worker:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Bad Way (Burns CPU):&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;go&quot;&gt;&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Done&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; checkStr &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt;pending&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// Process the log line&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// DANGER: If &apos;pending&apos; is empty, the code instantly falls through to here.&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// The loop restarts immediately, spinning endlessly and burning 100% CPU.&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To fix this, you simply remove the &lt;code class=&quot;language-text&quot;&gt;default&lt;/code&gt; case. Without it, the &lt;code class=&quot;language-text&quot;&gt;select&lt;/code&gt; statement blocks naturally. The Go scheduler recognizes that the goroutine has nothing to do, so it puts the routine to sleep until new data arrives in the channel or the context cancels.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Good Way (Zero CPU while waiting):&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;go&quot;&gt;&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Done&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; checkStr&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ok &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt;pending&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// If the channel is closed, ok is false and checkStr is &quot;&quot;.&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// Without this check, the loop would spin on zero-values&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// just like a busy wait — except harder to spot.&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;ok &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// If &apos;pending&apos; is open but empty, the goroutine safely sleeps here.&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// It wakes up the moment a new log line arrives or the channel closes.&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Level 3: Controlling Flow with a Rate Limiter&lt;/h3&gt;
&lt;p&gt;Sometimes the architectural challenge isn’t moving data too slowly; it is moving data too quickly. If your pipeline calls a third-party API that strictly enforces a limit of five requests per second, executing your tasks instantly will result in blocked connections or banned IP addresses.&lt;/p&gt;
&lt;p&gt;To mitigate this risk, we need to build a concurrency turnstile using &lt;code class=&quot;language-text&quot;&gt;time.Ticker&lt;/code&gt; to enforce a strict, mandatory delay between each operation. However, a common misconception here is that the upstream code simply keeps dumping data into the incoming channel at lightning speed while the rate limiter builds a massive internal queue. If that were true, your application would quickly exhaust its available memory by stockpiling thousands of unprocessed items.&lt;/p&gt;
&lt;p&gt;Instead, Go channels naturally provide a powerful system design feature called &lt;strong&gt;backpressure&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Because our rate limiter explicitly pauses to wait for the next timer tick, it completely stops pulling new items out of the &lt;code class=&quot;language-text&quot;&gt;pending&lt;/code&gt; channel. Once that incoming channel is full, the upstream reader is forced to pause because it physically cannot send any more data. The slow speed of the rate limiter naturally pushes back on the fast reader, which forces the entire pipeline to slow down and synchronize to a safe, predictable pace.&lt;/p&gt;
&lt;p&gt;Here is what that pipeline stage looks like when we wrap it in a proper function. Notice how the logic intentionally blocks reading the next item from the &lt;code class=&quot;language-text&quot;&gt;pending&lt;/code&gt; channel until the &lt;code class=&quot;language-text&quot;&gt;throttled&lt;/code&gt; channel successfully emits the current one alongside the timer tick.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;go&quot;&gt;&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// limiter takes a channel of pending requests and outputs them at a strict, steady pace.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;limiter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Context&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pending &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;chan&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; interval time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Duration&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;chan&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// The channel where we will send our rate-limited data downstream&lt;/span&gt;
    throttled &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;make&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;chan&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;go&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// Ensure we clean up the channel and ticker when we exit&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;throttled&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        
        ticker &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;NewTicker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;interval&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;defer&lt;/span&gt; ticker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Stop&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Done&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;
                
            &lt;span class=&quot;token comment&quot;&gt;// Step 1: Read a single pending item. &lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// If we are waiting for a tick below, this read cannot happen, which creates backpressure!&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; item&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ok &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt;pending&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;ok &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Upstream closed the channel, meaning all work is done&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                
                &lt;span class=&quot;token comment&quot;&gt;// Step 2: We have the item. Now, we must wait for the green light.&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt;ticker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;C&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                    &lt;span class=&quot;token comment&quot;&gt;// Step 3: The tick arrived. Now we safely send the item downstream.&lt;/span&gt;
                    &lt;span class=&quot;token comment&quot;&gt;// We must check context again here to prevent a deadlock if downstream crashes!&lt;/span&gt;
                    &lt;span class=&quot;token keyword&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; throttled &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt; item&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                    &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Done&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Done&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; throttled
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Level 4: Token Buckets and Handling Bursts&lt;/h3&gt;
&lt;p&gt;While our basic rate limiter does the job, it is too rigid for most real-world applications. If a user hasn’t made a single request in an hour, the system still forces them to wait 200 milliseconds before processing their very first action. Real-world APIs like AWS or &lt;a href=&quot;https://docs.stripe.com/rate-limits#handling-limiting-gracefully&quot;&gt;Stripe&lt;/a&gt; solve this by using the Token Bucket algorithm, which allows an initial burst of immediate traffic before the strict throttling takes over.&lt;/p&gt;
&lt;p&gt;To build a burstable rate limiter, we decouple our timing mechanism from our consumer logic by utilizing a buffered channel as our token bucket.&lt;/p&gt;
&lt;p&gt;This architecture requires three distinct components interacting together:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;The Bucket:&lt;/strong&gt; A buffered channel where the capacity represents our maximum allowed burst limit.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The Refiller:&lt;/strong&gt; A background goroutine that adds a single token to the bucket every interval.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The Consumer:&lt;/strong&gt; Our primary loop that simply attempts to read a token from the bucket before it processes the next incoming item.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here is what this looks like when we wrap it into a proper pipeline stage:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;go&quot;&gt;&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// burstableLimiter allows an initial burst of traffic, then throttles to a steady interval.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;burstableLimiter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Context&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pending &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;chan&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; interval time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Duration&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; burstLimit &lt;span class=&quot;token builtin&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;chan&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    throttled &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;make&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;chan&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    
    &lt;span class=&quot;token comment&quot;&gt;// 1. The Bucket: A buffered channel to hold our available tokens&lt;/span&gt;
    bucket &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;make&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;chan&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; burstLimit&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Pre-fill the bucket so the initial burst executes immediately&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; burstLimit&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        bucket &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// 2. The Refiller: Runs in the background and adds tokens&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;go&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        ticker &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; time&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;NewTicker&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;interval&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;defer&lt;/span&gt; ticker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Stop&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Done&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt;ticker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;C&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;token comment&quot;&gt;// We use &apos;default&apos; here to make this send non-blocking.&lt;/span&gt;
                &lt;span class=&quot;token comment&quot;&gt;// If the bucket is full, we simply drop the new token and move on.&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; bucket &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// 3. The Consumer: Reads data and waits for tokens&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;go&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;defer&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;throttled&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Done&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; item&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ok &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt;pending&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;ok &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Upstream closed the channel&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

                &lt;span class=&quot;token comment&quot;&gt;// Wait for a token. As long as the bucket is not empty, this is instant!&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt;bucket&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Done&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

                &lt;span class=&quot;token comment&quot;&gt;// We have the item and the token. Safely send it downstream.&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;select&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; throttled &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt; item&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Done&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; throttled
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Because we deliberately pre-filled the bucket during the initialization phase, the pipeline processes the first few items instantly. Once that initial burst depletes the bucket, the consumer naturally pauses and waits for the refiller goroutine to drop a new token into the bucket at our specified interval.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;⚠️ The Strategic Exception: Why We Used &lt;code class=&quot;language-text&quot;&gt;default&lt;/code&gt; in the Refiller&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This has confused me a lot. Earlier in this post, I explicitly called out the dangers of using a &lt;code class=&quot;language-text&quot;&gt;default&lt;/code&gt; case, noting how it creates a CPU-burning busy loop. However, you might have noticed that I intentionally used one inside the refiller goroutine. This isn’t a mistake; it’s a specific pattern called a non-blocking send.&lt;/p&gt;
&lt;p&gt;If the system experiences a lull in traffic, the token bucket quickly fills up to its maximum burst capacity. When the next timer tick fires, the refiller attempts to push another token into that full channel. If we simply wrote &lt;code class=&quot;language-text&quot;&gt;bucket &amp;lt;- struct{}{}&lt;/code&gt;, the operation would block. The refiller goroutine would freeze, waiting for a consumer to take a token.&lt;/p&gt;
&lt;p&gt;By wrapping that send operation in a &lt;code class=&quot;language-text&quot;&gt;select&lt;/code&gt; statement with an empty &lt;code class=&quot;language-text&quot;&gt;default&lt;/code&gt; case, we tell the Go runtime: &lt;em&gt;“Try to drop a token into the bucket. If the bucket is full, simply discard the token and go back to sleep until the next tick.”&lt;/em&gt; It gracefully enforces the maximum burst limit without locking up our background process.&lt;/p&gt;
&lt;p&gt;You might wonder why this doesn’t spike the CPU like the earlier example. In the Level 2 pitfall, the &lt;code class=&quot;language-text&quot;&gt;default&lt;/code&gt; case was a direct option in the main &lt;code class=&quot;language-text&quot;&gt;select&lt;/code&gt; block. Whenever the channel was empty, the &lt;code class=&quot;language-text&quot;&gt;select&lt;/code&gt; instantly fell through to the &lt;code class=&quot;language-text&quot;&gt;default&lt;/code&gt; case, which caused the outer &lt;code class=&quot;language-text&quot;&gt;for&lt;/code&gt; loop to spin continuously without pausing.&lt;/p&gt;
&lt;p&gt;In our refiller, the &lt;code class=&quot;language-text&quot;&gt;default&lt;/code&gt; case sits inside a nested &lt;code class=&quot;language-text&quot;&gt;select&lt;/code&gt; block. The outer &lt;code class=&quot;language-text&quot;&gt;select&lt;/code&gt; still forces the goroutine to pause and wait for the &lt;code class=&quot;language-text&quot;&gt;ticker.C&lt;/code&gt; channel. The program only evaluates that nested &lt;code class=&quot;language-text&quot;&gt;default&lt;/code&gt; case once per tick. This ensures the loop only executes every 200 milliseconds, keeping CPU usage negligible.&lt;/p&gt;
&lt;h3&gt;Final Thoughts and Best Practices&lt;/h3&gt;
&lt;p&gt;Stepping away from my usual infrastructure and reliability work to study these backend patterns has definitely changed how I look at low level design. Writing highly concurrent code in Go yields incredibly powerful applications, but it requires strict operational discipline to keep those systems stable under heavy load.&lt;/p&gt;
&lt;p&gt;As I worked through these iterations, I developed a practical checklist that I now use when reviewing any concurrent code.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Respect the Context to Prevent Goroutine Leaks:&lt;/strong&gt; In a real-world backend, requests frequently get cancelled, connections drop, or API timeouts expire. The &lt;code class=&quot;language-text&quot;&gt;context&lt;/code&gt; is how Go signals your application to stop working on a task that is no longer needed. If your goroutines ignore this cancellation signal while waiting to send or receive data on a channel, they will hang in the background forever. Over time, these stranded goroutines accumulate and cause silent memory leaks that degrade performance and eventually crash your application. Always pair your blocking channel operations with a &lt;code class=&quot;language-text&quot;&gt;case &amp;lt;-ctx.Done():&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Embrace Backpressure:&lt;/strong&gt; A well-designed pipeline does not rely on massive, bottomless queues that hoard memory. Instead, it leverages the natural blocking behavior of Go channels. If a downstream stage (like our rate limiter) slows down, let the channel fill up. That full channel forces the upstream reader to pause, which safely synchronizes the entire application to a sustainable pace.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Watch Out for the “Busy Wait” Trap:&lt;/strong&gt; Be extremely careful when using a &lt;code class=&quot;language-text&quot;&gt;default&lt;/code&gt; case inside an infinite &lt;code class=&quot;language-text&quot;&gt;for&lt;/code&gt; loop. Normally, a &lt;code class=&quot;language-text&quot;&gt;select&lt;/code&gt; statement pauses your goroutine until a channel is ready to send or receive data. If you add a &lt;code class=&quot;language-text&quot;&gt;default&lt;/code&gt; case, the &lt;code class=&quot;language-text&quot;&gt;select&lt;/code&gt; becomes non-blocking. When the channels are empty, the code instantly executes the default case and restarts the loop. This causes the loop to spin millions of times per second, which completely consumes your CPU. You should only use &lt;code class=&quot;language-text&quot;&gt;default&lt;/code&gt; when you explicitly want to make a single channel operation non-blocking—like dropping a token when a bucket is full—and only if the outer loop is already paused by something else, like a timer tick.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Understand the Trade-offs of Parallelism:&lt;/strong&gt; When you implement a Fan-Out pattern, you gain massive speed improvements and natural load balancing across multiple worker goroutines. However, because those workers operate independently, the final output order is not preserved. Always evaluate if your specific use case requires strict sequential processing before throwing a worker pool at the problem.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Documenting this progression helped me solidify my own understanding of these concurrency models. Hopefully, breaking down these concepts from a basic pipeline to a production-grade token bucket helps you build more resilient systems as well.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Terraform on CI - Part 2]]></title><description><![CDATA[Setting up a Terraform pipeline on CI — the practical challenges and how to solve them. Part 2 of the series.]]></description><link>https://absh.dev/terraform-on-ci-part2/</link><guid isPermaLink="false">https://absh.dev/terraform-on-ci-part2/</guid><pubDate>Sat, 07 Jun 2025 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;This blog is part 2 of a series of articles. In the last blog, we saw the benefits of running Terraform on CI. The following section will provide details on how to set up Terraform. Let’s zoom in on what challenges one can face while setting up the Terraform run on CI.&lt;/p&gt;
&lt;p&gt;Typically, Terraform is used to manage infrastructure in the Cloud. It can use a native provider such as &lt;a href=&quot;https://registry.terraform.io/providers/hashicorp/aws/latest/docs&quot;&gt;AWS Provider&lt;/a&gt;. Terraform can also utilize other tools to perform various automation tasks, such as &lt;a href=&quot;https://registry.terraform.io/providers/hashicorp/helm/latest/docs&quot;&gt;Helm&lt;/a&gt; for installing and managing resources in Kubernetes clusters, &lt;a href=&quot;https://registry.terraform.io/modules/cloudposse/ansible/null/latest&quot;&gt;Ansible&lt;/a&gt; for VM orchestration, and others. You may require more providers depending on your infrastructure requirements. Each provider has its setup caveats. We will cover some of these provider setups.&lt;/p&gt;
&lt;h2&gt;Terraform Version&lt;/h2&gt;
&lt;p&gt;Let’s start with the Terraform version itself. Locking and standardizing a Terraform version for everyone is a crucial step to avoid provider version conflict issues. Terraform version changes can affect the modules. If we create a state file using a Terraform version other than the decided-upon locked version, we may encounter issues with state file compatibility in newer versions.&lt;/p&gt;
&lt;p&gt;From the docs:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In general, Terraform will continue to work with a given state file across minor version updates. For major or minor releases, Terraform will update the state file version if required, and give an error if you attempt to run an older version of Terraform using an unsupported state file version.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;State file issues are more nasty to deal with than other minor issues, such as some backend flags being deprecated: &lt;a href=&quot;https://developer.hashicorp.com/terraform/language/v1.8.x/upgrade-guides#s3-backend-authentication-changes&quot;&gt;Example&lt;/a&gt;. Such issues need to be dealt with on a case-by-case basis. If you are able to run &lt;code class=&quot;language-text&quot;&gt;terraform init&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;terraform plan&lt;/code&gt; in all modules without any error, it means you have chosen the right version for the current IaC codebase.&lt;/p&gt;
&lt;h2&gt;Cloud provider access&lt;/h2&gt;
&lt;p&gt;Terraform will need to talk to the cloud providers via CI. Proper authentication mechanisms must be in place for the “Plan” and “Apply” commands to run without any access errors. To understand this in detail, I’ll take the Example of AWS, but the idea remains the same for other providers. I will use the example of GitHub Actions to demonstrate how to run Terraform on a CI/CD pipeline.&lt;/p&gt;
&lt;p&gt;When an Infra admin runs Terraform from a local machine to make Infra changes, they typically use their identity (AWS SSO Profile, for example) to authenticate with AWS. AWS Terraform provider can get auth configuration from &lt;a href=&quot;https://registry.terraform.io/providers/hashicorp/aws/latest/docs#authentication-and-configuration&quot;&gt;several&lt;/a&gt; sources with a pre-defined priority order. We can also use any listed auth mechanisms to authenticate terraform on CI.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Setup&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The first setup involves creating an AWS directory in CI (&lt;code class=&quot;language-text&quot;&gt;~/.aws&lt;/code&gt;) and an AWS configuration file that the &lt;a href=&quot;https://registry.terraform.io/providers/hashicorp/aws/latest/docs&quot;&gt;TF AWS Provider&lt;/a&gt; can use. Inside this file, we should mention a role that has access to create/destroy/change all kinds of infra in your AWS account.&lt;/p&gt;
&lt;p&gt;This is how the role policy should look like:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;Statement&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;Action&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;*&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;Effect&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Allow&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;Resource&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;*&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;Sid&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;Version&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2012-10-17&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We have given &lt;code class=&quot;language-text&quot;&gt;*/*&lt;/code&gt; permissions to this role. However, we can also restrict access by allowing selective actions on specific resources, providing better control over infrastructure and costs.&lt;/p&gt;
&lt;p&gt;Let’s call this IAM role &lt;code class=&quot;language-text&quot;&gt;core-tf-runner-role&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We assume this role in CI to provision infrastructure in our AWS account. We will need to allow the Github Runners to assume this role. We can achieve that via a trust relationship. AWS has a nice blog on how to achieve just that. You can refer to it &lt;a href=&quot;https://aws.amazon.com/blogs/security/use-iam-roles-to-connect-github-actions-to-actions-in-aws/&quot;&gt;here&lt;/a&gt;. It involves configuring an OIDC identity provider inside an AWS account. This setup enables the usage of the IAM role and short-term credentials. It will allow the GitHub runner to assume the &lt;code class=&quot;language-text&quot;&gt;core-tf-runner-role.&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/4c2ed3c1475bab0a98d41f0b31bb36b7/terraform_ci_0.svg&quot; alt=&quot;Conceptual Flow&quot;&gt;&lt;/p&gt;
&lt;p&gt;On CI, we need to create a configuration file in the format below to provide it with an AWS identity. The AWS Profile should assume the role we created above.&lt;/p&gt;
&lt;p&gt;Example Config file:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;profile core_aws_account&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
role_arn &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; arn:aws:iam::dev-acnt-id:role/core-tf-runner-role
credential_source &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Environment
region &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; us-west-1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We have multiple ways to make his file available on CI, such as pre-baking in the CI image, importing as a GitHub action step, or using shell magic, among others. For the sake of simplicity, I assume that we are creating this file from a script. Here is a demonstrative GitHub Actions code block that describes the approach.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; create aws directory and a config file
  &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; mkdir ~/.aws &lt;span class=&quot;token important&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; touch ~/.aws/config

&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Render AWS configs
  &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token scalar string&quot;&gt;
    &amp;lt;&amp;lt;your script to load the Config file inside ~/.aws/config&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Setup terraform
  &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; hashicorp/setup&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;terraform@v3
  &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;terraform_version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;1.6.0&apos;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# locking the version&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;With this setup ready, we can run commands inside Terraform modules on the GitHub Actions CI job.&lt;/p&gt;
&lt;p&gt;Below is an example Terraform module that uses the AWS profile we just set up. Here, the state file is in the same AWS account where we are creating other infrastructure resources.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;//terraform.tf&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;terraform&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;required_version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;~&gt; 1.6.0&quot;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;required_providers&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;aws&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;source&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;hashicorp/aws&quot;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;~&gt; 4.59.0&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;backend&lt;span class=&quot;token type variable&quot;&gt; &quot;s3&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;bucket&lt;/span&gt;              &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;&amp;lt;bucket-name&gt;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;key&lt;/span&gt;                 &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;&amp;lt;state path&gt;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;region&lt;/span&gt;              &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;us-west-1&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;profile&lt;/span&gt;             &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;core_aws_account&quot;&lt;/span&gt;   &lt;span class=&quot;token comment&quot;&gt;//access provided via ~/.aws/config file&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;use_legacy_workflow&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;//provider.tf&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;provider&lt;span class=&quot;token type variable&quot;&gt; &quot;aws&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;region&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.region
  &lt;span class=&quot;token property&quot;&gt;profile&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;core_aws_account&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;//main.tf&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;aws_s3_bucket&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;example&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;bucket&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;my-tf-test-bucket&quot;&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;tags&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;Name&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;My bucket&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;Environment&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Dev&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Multi AWS Account setup&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Suppose your modules have multiple AWS account connectivity requirements. In that case, it’s best to create one tf-runner role per AWS account for better control and management, e.g., prod-tf-runner-role, Security-Tf-Runner-Role, dev-tf-runner-role. The config file expands correspondingly.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;profile dev_aws_account&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
role_arn &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; arn:aws:iam::dev-acnt-id:role/dev-tf-runner-role
credential_source &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Environment
region &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; us-west-1

&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;profile prod_aws_account&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
role_arn &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; arn:aws:iam::prod-acnt-id:role/prod-tf-runner-role
credential_source &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Environment
region &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; us-west-1

&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;profile security_aws_account&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
role_arn &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; arn:aws:iam::prod-acnt-id:role/security-tf-runner-role
credential_source &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Environment
region &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; us-west-1&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We can establish the same trust relationship with Github in each role, but that can be tedious to manage. We can “DRY” it further by creating an intermediate role that can assume all these runner roles and can be assumed by Github Action runner. I’ll call this intermediate role “core-tf-runner-role” here.&lt;/p&gt;
&lt;p&gt;Conceptually:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/bf58f5a4e97a2245950aeb0c8a61f5dd/terraform_ci_2.svg&quot; alt=&quot;Conceptual Flow&quot;&gt;&lt;/p&gt;
&lt;p&gt;To achieve this flow, we will need to add a trust relationship between &lt;code class=&quot;language-text&quot;&gt;core-tf-runner-role&lt;/code&gt; and other runner roles.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// create this trust relationship with all account runner roles&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;Version&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2012-10-17&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;Statement&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;Sid&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;Effect&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Allow&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;Principal&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;AWS&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;arn:aws:iam::ACCNT_ID:role/core-tf-runner-role&quot;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;Action&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;sts:AssumeRole&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Additionally, we need to allow the core-tf-runner-role to assume other roles by attaching an appropriate policy:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;Version&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2012-10-17&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;Statement&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;Effect&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Allow&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;Action&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;sts:AssumeRole&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;Resource&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;arn:aws:iam::dev-acnt-id:role/dev-tf-runner-role&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;arn:aws:iam::prod-acnt-id:role/prod-tf-runner-role&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;arn:aws:iam::security-acnt-id:role/security-tf-runner-role&quot;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;With this setup, we can now use the AWS config file to handle the last leg of role chaining.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/f23fc3185cbd6de35ea9dddd83361e42/terraform_ci_1.svg&quot; alt=&quot;Role Chaining Flow&quot;&gt;&lt;/p&gt;
&lt;p&gt;To recap, we have set the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Created runner roles per AWS Account with &lt;code class=&quot;language-text&quot;&gt;*/*&lt;/code&gt; permissions.&lt;/li&gt;
&lt;li&gt;Created &lt;code class=&quot;language-text&quot;&gt;core-tf-runner-role&lt;/code&gt; which can assume Runner roles in various accounts&lt;/li&gt;
&lt;li&gt;Allowed Github Action to assume this &lt;code class=&quot;language-text&quot;&gt;core-tf-runner-role&lt;/code&gt; via OIDC IdP flow&lt;/li&gt;
&lt;li&gt;Created an AWS Config file on CI to allow TF AWS Providers to authenticate&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The GitHub action file is where the role chaining happens.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; create aws directory and a config file
  &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; mkdir ~/.aws &lt;span class=&quot;token important&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; touch ~/.aws/config

&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Render AWS configs
  &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token scalar string&quot;&gt;
    &amp;lt;&amp;lt;your script to load the Config file inside ~/.aws/config&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Setup terraform
  &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; hashicorp/setup&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;terraform@v3
  &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;terraform_version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;1.6.0&apos;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# locking the version&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Assume runner role
  &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; aws&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;actions/configure&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;aws&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;credentials@v2
  &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;role-to-assume&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; arn&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;aws&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;iam&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;AWS_ACNT_ID&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;role/core&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;tf&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;runner&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;role
    &lt;span class=&quot;token key atrule&quot;&gt;audience&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; audString
    &lt;span class=&quot;token key atrule&quot;&gt;aws-region&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; us&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;west&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# next steps will run plan / apply commands&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;CI Workflow Design&lt;/h2&gt;
&lt;p&gt;Now that we have authentication sorted out, let’s design the CI triggers. A well-designed workflow should:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Run the terraform plan on all pull requests&lt;/li&gt;
&lt;li&gt;Run the terraform apply when the pull request is merged&lt;/li&gt;
&lt;li&gt;Handle multiple Terraform module changes in a single pull request&lt;/li&gt;
&lt;li&gt;Provide clear output for review in all actions&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We will also need a runner with appropriate network connectivity in various AWS accounts and VPCs. Let’s keep the runner setup part out of scope for the sake of brevity in explaining the topic of this blog. Let’s consolidate all these in a GitHub Actions workflow file.&lt;/p&gt;
&lt;h3&gt;Terraform Plan on Pull Requests&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Terraform Plan

&lt;span class=&quot;token key atrule&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;pull_request&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# run this wf on PRs on this repo&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;branches&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; main

&lt;span class=&quot;token key atrule&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;terraform-plan&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; internal&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;runner &lt;span class=&quot;token comment&quot;&gt;# A runner with proper networking setup&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; actions/checkout@v3

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Create AWS config
        &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token scalar string&quot;&gt;
          mkdir -p ~/.aws
          cat &gt; ~/.aws/config &amp;lt;&amp;lt; EOF
          [profile dev_aws_account]
          role_arn = arn:aws:iam::dev-acnt-id:role/dev-tf-runner-role
          credential_source = Environment
          region = us-west-1&lt;/span&gt;

          &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;profile prod_aws_account&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
          role_arn = arn&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;aws&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;iam&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;prod&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;acnt&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;role/prod&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;tf&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;runner&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;role
          credential_source = Environment
          region = us&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;west&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;

          &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;profile security_aws_account&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
          role_arn = arn&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;aws&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;iam&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;security&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;acnt&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;role/security&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;tf&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;runner&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;role
          credential_source = Environment
          region = us&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;west&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
          EOF

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Setup Terraform
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; hashicorp/setup&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;terraform@v3
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;terraform_version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;1.6.0&apos;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# your desired version&lt;/span&gt;

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Assume Terraform runner role
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; aws&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;actions/configure&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;aws&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;credentials@v2
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;role-to-assume&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; arn&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;aws&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;iam&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;AWS_ACNT_ID&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;role/core&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;tf&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;runner&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;role &lt;span class=&quot;token comment&quot;&gt;# allowed to assume via OIDC IdP flow&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;audience&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; audString
          &lt;span class=&quot;token key atrule&quot;&gt;aws-region&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; us&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;west&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Get changed files &lt;span class=&quot;token comment&quot;&gt;# relative to main branch&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; changed&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;files
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; tj&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;actions/changed&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;files@v40

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Create directory to store plan out file
        &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token scalar string&quot;&gt;
          mkdir -p artifact&lt;/span&gt;

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Run PLAN on changed leaf directories &lt;span class=&quot;token comment&quot;&gt;# custom script&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token scalar string&quot;&gt;
          python3 change_detection.py ${{ steps.changed-files.outputs.all_changed_files }}&lt;/span&gt;

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Upload Artifact
        &lt;span class=&quot;token key atrule&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; github.ref == &apos;refs/heads/main&apos; &lt;span class=&quot;token comment&quot;&gt;# upload artifact only when branch is merged to main&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; actions/upload&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;artifact@v4
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; terraform&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;pr&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;artifact
          &lt;span class=&quot;token key atrule&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; artifact&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This workflow does several important things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Assumes the core tf runner role via OIDC IdP flow (short-term credentials used)&lt;/li&gt;
&lt;li&gt;Setup terraform at a particular version&lt;/li&gt;
&lt;li&gt;Find all changed files relative to the target branch(main)&lt;/li&gt;
&lt;li&gt;Run a script that takes an input list of all the changed files.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;1. detect the changed terraform modules in the PR from the list of changed files provided as input
2. for each changed module(here in my example, the leaf directories) in the IaC repo:
 - Runs `terraform init` utilising [chdir flag](https://developer.hashicorp.com/terraform/cli/commands#switching-working-directory-with-chdir)
 - Runs `terraform plan` and saves the plan out
3. Upload the plan-out file to GitHub Artifact to utilize it in a separate GitHub workflow.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here is the &lt;code class=&quot;language-text&quot;&gt;change_detection.py&lt;/code&gt; script&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;py&quot;&gt;&lt;pre class=&quot;language-py&quot;&gt;&lt;code class=&quot;language-py&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; sys
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; subprocess
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; json
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; utils


&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    tf_files_changed &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; utils&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get_tf_files&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sys&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;argv&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    changed_directories &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; utils&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;get_changed_directories&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tf_files_changed&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    paths_references &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;# print the changed leaf&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; item &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; changed_directories&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;Detected changed directories: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; item &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; changed_directories&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;\n===============================Processing: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;===============================\n&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; subprocess&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;run&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;terraform&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;-chdir=&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;init&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                stdout&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;subprocess&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PIPE&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                stderr&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;subprocess&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;STDOUT&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                text&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;stdout&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;check_returncode&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;# raises if non zero exit code&lt;/span&gt;

        plan_file_name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;_&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;join&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;split&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        tf_plan_cmd &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;terraform&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;-chdir=&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;plan&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;-out&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;plan_file_name&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; subprocess&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;run&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tf_plan_cmd&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                stdout&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;subprocess&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PIPE&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                stderr&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;subprocess&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;STDOUT&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                                text&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;stdout&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;check_returncode&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;# raises if non zero exit code&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;#move plan file to artifact directory&lt;/span&gt;
        result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; subprocess&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;run&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;mv&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;item&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;plan_file_name&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;artifact/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;plan_file_name&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            stdout&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;subprocess&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PIPE&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            stderr&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;subprocess&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;STDOUT&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            text&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;stdout&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;check_returncode&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;# raises if non zero exit code&lt;/span&gt;
        paths_references&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;plan_file_name&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; item

    &lt;span class=&quot;token keyword&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;artifact/path_ref.json&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;w&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; fp&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        json&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dump&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;paths_references&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; fp&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;


&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; __name__ &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;__main__&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    main&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The code is self-explanatory. I have used some utils functions. The actual implementation will depend on how you have organized the IaC repository. We are saving the plan-out file in GitHub Artifact to apply the same changes in a later stage. Github Artifact is one way to save and use the plan file. You can use other methods to manage the plan output file.&lt;/p&gt;
&lt;h3&gt;Terraform Apply&lt;/h3&gt;
&lt;p&gt;The admins can decide how they want to trigger the Apply. It could be manual or automated. Here is an example using manual Github Workflow dispatch, taking the workflow run ID of the planning stage we showed earlier as input.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Terraform APPLY

&lt;span class=&quot;token key atrule&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;workflow_dispatch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;inputs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;plan_workflow_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; string
        &lt;span class=&quot;token key atrule&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; PLAN workflow run id

&lt;span class=&quot;token key atrule&quot;&gt;jobs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;terraform-apply&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;runs-on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; internal&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;runner &lt;span class=&quot;token comment&quot;&gt;#A runner with proper networking setup&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;steps&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; actions/checkout@v3

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Create AWS config
        &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token scalar string&quot;&gt;
          mkdir -p ~/.aws
          cat &gt; ~/.aws/config &amp;lt;&amp;lt; EOF
          [profile dev_aws_account]
          role_arn = arn:aws:iam::dev-acnt-id:role/dev-tf-runner-role
          credential_source = Environment
          region = us-west-1&lt;/span&gt;

          &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;profile prod_aws_account&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
          role_arn = arn&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;aws&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;iam&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;prod&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;acnt&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;role/prod&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;tf&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;runner&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;role
          credential_source = Environment
          region = us&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;west&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;

          &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;profile security_aws_account&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
          role_arn = arn&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;aws&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;iam&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;security&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;acnt&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;role/security&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;tf&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;runner&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;role
          credential_source = Environment
          region = us&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;west&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
          EOF

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Setup Terraform
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; hashicorp/setup&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;terraform@v3
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;terraform_version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;1.6.0&apos;&lt;/span&gt;

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Assume Terraform runner role
        &lt;span class=&quot;token key atrule&quot;&gt;uses&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; aws&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;actions/configure&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;aws&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;credentials@v2
        &lt;span class=&quot;token key atrule&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;role-to-assume&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; arn&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;aws&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;iam&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;AWS_ACNT_ID&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;role/core&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;tf&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;runner&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;role &lt;span class=&quot;token comment&quot;&gt;# allowed to assume via OIDC IdP flow&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;audience&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; audString
          &lt;span class=&quot;token key atrule&quot;&gt;aws-region&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; us&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;west&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;

      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; APPLY plan out files
        &lt;span class=&quot;token key atrule&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; terraform_apply
        &lt;span class=&quot;token key atrule&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token scalar string&quot;&gt;
          python3 apply.py&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here is a similar script running the apply command. It reads each plan file in the artifact. It moves those plan files to their respective modules and applies them one by one.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;py&quot;&gt;&lt;pre class=&quot;language-py&quot;&gt;&lt;code class=&quot;language-py&quot;&gt;&lt;span class=&quot;token triple-quoted-string string&quot;&gt;&quot;&quot;&quot;This module reads the artifact contents and moves
the plan files to proper leaf directories and applies
the plan out files one by one&quot;&quot;&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; subprocess
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; json

path_ref_filepath &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;artifact/path_ref.json&apos;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;move_plan_out_to_proper_dir&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;plan_file_name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; path&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    plan_mv_cmd &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;mv&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&apos;artifact/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;plan_file_name&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&apos;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/plan.out&apos;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; subprocess&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;run&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;plan_mv_cmd&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                            stdout&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;subprocess&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PIPE&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                            stderr&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;subprocess&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;STDOUT&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                            text&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;stdout&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;check_returncode&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;


&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;run_terraform_init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; subprocess&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;run&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;terraform&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;-chdir=&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;init&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                            stdout&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;subprocess&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PIPE&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                            stderr&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;subprocess&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;STDOUT&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                            text&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;stdout&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;check_returncode&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;


&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;show_plan_file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; subprocess&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;run&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;terraform&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;show&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;plan.out&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                            cwd&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                            stdout&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;subprocess&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PIPE&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                            stderr&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;subprocess&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;STDOUT&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                            text&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;stdout&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;check_returncode&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;


&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;run_terraform_apply&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; subprocess&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;run&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;terraform&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;-chdir=&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;apply&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;plan.out&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        stdout&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;subprocess&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;PIPE&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        stderr&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;subprocess&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;STDOUT&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        text&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;stdout&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;check_returncode&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;


&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;with&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;path_ref_filepath&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        file_contents &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;read&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    successful_apply &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    path_ref &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; json&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;loads&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;file_contents&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; plan_file_name &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; path_ref&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        path &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; path_ref&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;plan_file_name&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;token string-interpolation&quot;&gt;&lt;span class=&quot;token string&quot;&gt;f&quot;\n===============================Processing: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;===============================\n&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        move_plan_out_to_proper_dir&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;plan_file_name&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;plan_file_name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; path&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        run_terraform_init&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;path&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        show_plan_file&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;path&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        run_terraform_apply&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;path&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        successful_apply&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;append&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;apply successful in these directories:&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; successful_apply&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;


&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; __name__ &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;__main__&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    main&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;After this job is successful, the pipeline will have applied the changes to each module. The output logs will display the logs from the Terraform run that achieves our desired goal.&lt;/p&gt;
&lt;p&gt;In conclusion, you can use multiple approaches to model your CI workflow; the above is one example. A centralized Terraform execution environment helps achieve a collaborative infrastructure workflow for engineering teams. The shared state files ensure that changes are applied serially to common resources and that everyone on the team is using the same tooling, thereby avoiding configuration drift. The GitOps model scales with your team. It also allows for faster collaboration on the infrastructure code.&lt;/p&gt;
&lt;h2&gt;Best Practices and Learnings&lt;/h2&gt;
&lt;p&gt;Based on our experience running Terraform on CI, here are some best practices and learnings from this workflow:&lt;/p&gt;
&lt;h3&gt;Module structure&lt;/h3&gt;
&lt;p&gt;Organize your Terraform code into small, logical modules that can be updated without affecting other modules (single reason to change). This separation enables the parallel execution of Plan and Apply on CI on multiple modules in different CI jobs, reducing the cascading effect of infrastructure changes.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;terraform/
  ├── networking/         # VPC, subnets, etc.
  ├── compute/            # EC2, ASGs, etc.
  ├── database/           # RDS, DynamoDB, etc.
  ├── observability/      # CloudWatch, Grafana, etc.
  └── iam/                # IAM roles and policies&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Handle Terraform state locking&lt;/h3&gt;
&lt;p&gt;When multiple jobs run in parallel and multiple developers work on infrastructure, state locking becomes essential. We store our terraform state on S3 buckets.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;terraform&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;backend&lt;span class=&quot;token type variable&quot;&gt; &quot;s3&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;bucket&lt;/span&gt;         &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;terraform-state-bucket&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;key&lt;/span&gt;            &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;path/to/state/file&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;region&lt;/span&gt;         &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;us-west-1&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;profile&lt;/span&gt;        &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;core_aws_account&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Separate environments with workspaces or directories&lt;/h3&gt;
&lt;p&gt;For multi-environment setups, use directories and sub-directories. Keeping environments separate makes testing easy, brings confidence in changes, and facilitates easier debugging and rollback.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;terraform/
  ├── dev/
  │   ├── networking/
  │   └── kubernetes/
  ├── prod/
  │   ├── networking/
  │   └── kubernetes/&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Use variables for cross-account resource references&lt;/h3&gt;
&lt;p&gt;When resources in one account need to reference resources in another account, use variables and data sources to facilitate this connection. It reduces confusion in resource naming and clearly expresses intent.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# In account A&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;output&lt;span class=&quot;token type variable&quot;&gt; &quot;vpc_id&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;value&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; aws_vpc.main.id
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# In account B&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;variable&lt;span class=&quot;token type variable&quot;&gt; &quot;account_a_vpc_id&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;VPC ID from Account A&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;data &lt;span class=&quot;token type variable&quot;&gt;&quot;aws_vpc&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;from_account_a&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;provider&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; aws.account_a
  &lt;span class=&quot;token property&quot;&gt;id&lt;/span&gt;       &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; var.account_a_vpc_id
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Security considerations&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Limit the permissions of the CI role to only what’s necessary. &lt;code class=&quot;language-text&quot;&gt;*/*&lt;/code&gt; should be used cautiously.&lt;/li&gt;
&lt;li&gt;Consider manual approval for sensitive changes&lt;/li&gt;
&lt;li&gt;Be cautious with terraform output&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Multi Cloud Infra&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;In case your infrastructure spans multiple cloud providers, the approach of role chaining and change detection remains the same.&lt;/li&gt;
&lt;li&gt;The module organization becomes a key aspect in the smooth functioning of the pipeline&lt;/li&gt;
&lt;li&gt;You may need to repeat the similar role chaining respective to other cloud providers. It will become easier if the underlying cloud provider supports a similar short-term credentials approach.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Running Terraform on CI offers numerous benefits for infrastructure management:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Standardized environments for all terraform runs&lt;/li&gt;
&lt;li&gt;Improved collaboration through PR reviews&lt;/li&gt;
&lt;li&gt;Version control and history for all infrastructure changes&lt;/li&gt;
&lt;li&gt;Reduced manual toil and human error&lt;/li&gt;
&lt;li&gt;Better onboarding experience for new team members&lt;/li&gt;
&lt;li&gt;Reduces knowledge silos&lt;/li&gt;
&lt;li&gt;Gives a solid base for your IaC to scale as the team/company grows&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can create a robust infrastructure-as-code pipeline that scales with your organization by setting up proper authentication and workflow design and following best practices. This approach democratizes infrastructure changes while maintaining security and control over critical resources.&lt;/p&gt;
&lt;p&gt;The journey to effective Terraform in CI may require an initial investment in setup. However, the long-term benefits of increased productivity, reliability, and team satisfaction make the effort worthwhile.&lt;/p&gt;
&lt;p&gt;In part 3, we will explore how to use Ansible and Terraform to manage a fleet of Virtual machines.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Terraform on CI - Part 1]]></title><description><![CDATA[Why running Terraform on CI beats running it from laptops — the case for automating infrastructure changes safely. Part 1 of a series.]]></description><link>https://absh.dev/terraform-on-ci-part1/</link><guid isPermaLink="false">https://absh.dev/terraform-on-ci-part1/</guid><pubDate>Thu, 07 Nov 2024 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;We at &lt;a href=&quot;https://www.pixxel.space/&quot;&gt;Pixxel&lt;/a&gt; heavily use &lt;a href=&quot;https://www.terraform.io/&quot;&gt;Terraform&lt;/a&gt; to create, change and destroy infrastructure. Terraform allows you to declare infrastructure as code in a language called HCL. Once you have expressed your IaC in code, you can commit to a version control. You can get it reviewed collaboratively; if things go wrong, you can roll it back. It allows us to move fast, following principles similar to software engineering, including clean code practices. With that said, the following section will provide a basic introduction to Terraform and explain how we use Terraform to democratize infrastructure at Pixxel.&lt;/p&gt;
&lt;h2&gt;Typical Terraform Workflow&lt;/h2&gt;
&lt;p&gt;Terraform allows you to dry-run your changes before actually applying them. This is called a “Plan.” The result of running a plan is a human-readable diff of the changes.&lt;/p&gt;
&lt;p&gt;Here is the most straightforward Terraform file. It uses the &lt;code class=&quot;language-text&quot;&gt;kreuzwerker/docker&lt;/code&gt; provider to pull an image &lt;code class=&quot;language-text&quot;&gt;nginx:latest&lt;/code&gt; and create an nginx container named &lt;code class=&quot;language-text&quot;&gt;psyduck&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# main.tf&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;terraform&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;required_providers&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;docker&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;source&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;kreuzwerker/docker&quot;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;3.0.2&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;provider&lt;span class=&quot;token type variable&quot;&gt; &quot;docker&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;host&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;unix:///var/run/docker.sock&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;docker_image&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;nginx&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;nginx:latest&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;resource &lt;span class=&quot;token type variable&quot;&gt;&quot;docker_container&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;nginx&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;image&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;nginx&quot;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;psyduck&quot;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;ports&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;internal&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;external&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you run &lt;code class=&quot;language-text&quot;&gt;terraform plan&lt;/code&gt; from the root directory of this file. You get the following output:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # docker_container.nginx will be created
  + resource &quot;docker_container&quot; &quot;nginx&quot; {
      + attach                                      = false
      + bridge                                      = (known after apply)
      + command                                     = (known after apply)
      + container_logs                              = (known after apply)
      + container_read_refresh_timeout_milliseconds = 15000
      + entrypoint                                  = (known after apply)
      + env                                         = (known after apply)
      + exit_code                                   = (known after apply)
      + hostname                                    = (known after apply)
      + id                                          = (known after apply)
      + image                                       = &quot;nginx&quot;
      + init                                        = (known after apply)
      + ipc_mode                                    = (known after apply)
      + log_driver                                  = (known after apply)
      + logs                                        = false
      + must_run                                    = true
      + name                                        = &quot;psyduck&quot;
      + network_data                                = (known after apply)
      + read_only                                   = false
      + remove_volumes                              = true
      + restart                                     = &quot;no&quot;
      + rm                                          = false
      + runtime                                     = (known after apply)
      + security_opts                               = (known after apply)
      + shm_size                                    = (known after apply)
      + start                                       = true
      + stdin_open                                  = false
      + stop_signal                                 = (known after apply)
      + stop_timeout                                = (known after apply)
      + tty                                         = false
      + wait                                        = false
      + wait_timeout                                = 60

      + ports {
          + external = 80
          + internal = 80
          + ip       = &quot;0.0.0.0&quot;
          + protocol = &quot;tcp&quot;
        }
    }

  # docker_image.nginx will be created
  + resource &quot;docker_image&quot; &quot;nginx&quot; {
      + id          = (known after apply)
      + image_id    = (known after apply)
      + name        = &quot;nginx:latest&quot;
      + repo_digest = (known after apply)
    }

Plan: 2 to add, 0 to change, 0 to destroy.

─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

Note: You didn&apos;t use the -out option to save this plan, so Terraform can&apos;t guarantee to take exactly these actions if you run &quot;terraform apply&quot; now.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can apply the plan by running &lt;code class=&quot;language-text&quot;&gt;terraform apply&lt;/code&gt; if the plan looks good. You will observe the following output:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only &apos;yes&apos; will be accepted to approve.

  Enter a value: yes

docker_image.nginx: Creating...
docker_container.nginx: Creating...
docker_image.nginx: Still creating... [10s elapsed]
docker_container.nginx: Still creating... [10s elapsed]
docker_image.nginx: Creation complete after 10s [id=sha256:4b196525bd3cc6aa7a72ba63c6c2ae6d957b57edd603a7070c5e31f8e63c51f9nginx:latest]
docker_container.nginx: Creation complete after 10s [id=cf2b32c1c0c3b3aa53386949fe5ca35b19b753a648e34d582fd35b5f5a858cf1]

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Terraform stores the current infrastructure state in a “state file” named “terraform.tfstate” extension. When you run the plan, it will show the diff between the current code and the state file. When you accept and apply the diff, Terraform will make the appropriate changes to update the infra and state file. The code and state remain in sync. Terraform uses the &lt;a href=&quot;https://registry.terraform.io/browse/providers&quot;&gt;Providers&lt;/a&gt; to make the infra changes.&lt;/p&gt;
&lt;p&gt;To run Terraform collaboratively, a team of SRE/DevOps engineers may choose to share the same state file with everyone in the team - by putting it in a shared bucket, e.g., an S3 bucket. This way, multiple people making changes to infra will not accidentally overwrite each other. When someone runs a plan/apply action, Terraform acquires a lock on the state file. When others try to access the state, they will see an error ensuring the serializability of infra changes. We can share the state file by mentioning the &lt;code class=&quot;language-text&quot;&gt;backend&lt;/code&gt; block inside the terraform file.&lt;/p&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;hcl&quot;&gt;&lt;pre class=&quot;language-hcl&quot;&gt;&lt;code class=&quot;language-hcl&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# main.tf&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;terraform&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;required_providers&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;docker&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;source&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;kreuzwerker/docker&quot;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;3.0.2&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;backend&lt;span class=&quot;token type variable&quot;&gt; &quot;s3&quot; &lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;bucket&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;mybucket&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;key&lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;path/to/my/key&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;region&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;us-east-2&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Terraform will try to connect to this bucket and proceed to it using the configured authentication mechanism. In later sections, we will explore authentication in depth.&lt;/p&gt;
&lt;h2&gt;Going one step beyond&lt;/h2&gt;
&lt;p&gt;Even with a collaborative workflow, the responsibility of making infra changes relies solely on individual DevOps/SRE team members. This can be a good or bad thing, depending on how one views it.&lt;/p&gt;
&lt;p&gt;It is good because only a particular set of people have to do infrastructure work, giving other developers (such as Backend, ML, and AI engineers) time to focus on their core problems, which are shipping out new features and fixing bugs. It is bad because only a particular set of people can make infrastructure changes 😄, which creates a blocker for other teams, even for the simplest tasks.&lt;/p&gt;
&lt;p&gt;Most of the time, the infra changes requested by the developer and other teams are &lt;a href=&quot;https://sre.google/sre-book/eliminating-toil/&quot;&gt;ops/toil&lt;/a&gt; in nature, such as giving or removing access, increasing disk sizes, creating SSH users on servers, or setting up CI/CD for a new service. Infra teams can set up local automation scripts for such ops tasks. They can even share the scripts with other teammates.&lt;/p&gt;
&lt;p&gt;The issue with local runs is that the environment can break easily. For example, provider version mismatch, unintended package updates, corrupted config files, and cloud authentication issues can break local automation environments due to regressions. These issues can also be a problem for Terraform modules. Such regressions can delay resolving these ops requests and lead to X-Y problem scenarios. The Engineer wanted to complete the ops task but found themselves debugging the correct config file values. Situations like these can cause ops fatigue if not appropriately dealt with. Introducing a new member to the team increases the problem many folds. An experienced teammate must personally teach the new member all the “tips” and “tricks” of running Terraform. The complexity of the infrastructure landscape can also add up to more regressions, such as many cloud providers in use and legacy infra code. It just adds up the time to resolve the ops task at hand.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;That’s where terraform on CI can help.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;We can run the terraform automation scripts on a CI pipeline. In that case, we can drastically reduce the average time to resolve ops tasks. Furthermore, developers can get unblocked by contributing to such automation since the code speaks for itself! It saves much time for everyone. Moreover, running Terraform on a CI is just like any other recurring software engineering task that needs to run in an automated way if appropriately designed. In the upcoming section, I will explain how we put Terraform on CI at Pixxel. But let’s look at how the terraform workflow stated in the previous section changes. Below are the updated steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;You make the required code changes to the relevant terraform modules.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You raise a PR with such changes&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The CI detects the changed modules against the main branch.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The CI runs the Terraform plan on these modules using the same shared state file.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Other team members can review the plan. You can also perform static code checks such as linting and formatting.
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/7b1f2ba759f6a7a59c860b9dc971c98b/11a8f/ci_0.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 29.72972972972973%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAABYlAAAWJQFJUiTwAAABJElEQVR42o2P607CMBiGd/+3YWKUhMRbMBHCYQ7GBmOndu2682CgLhKUvZQSNf7SJk/SL/n6vG+1LMsQhgSMcQUhFFHEcDp1+M/52np7/4AvdtCaZo+qqlGUpaRC27Y4HA7ouh/h5f4Xx88T9u0RGk8zPD4NEFAGL4xAmADlAnFaKERWIK822O5fUcvwunlRVNvdL1haIykaaLYb4Ob2HjPLUehzGxPDgrny5LzGYuXDcgK4hCNgCYLoCo1zsKRU8LSE6QrYfnIV3vX6GD+biunMwnBsYGosZPMRhhMDg5GOkT5XIXPbVQFBJKSsQCQu4hzGisP2BDQnpOj1H2DJJoulJ5d91XTphvLxGqYS+ErgkRg+jSHyGmm5lV/cfOPQHA5JcQazOriOZKQP+gAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;PR Checks example&quot;
        title=&quot;&quot;
        src=&quot;/static/7b1f2ba759f6a7a59c860b9dc971c98b/fcda8/ci_0.png&quot;
        srcset=&quot;/static/7b1f2ba759f6a7a59c860b9dc971c98b/12f09/ci_0.png 148w,
/static/7b1f2ba759f6a7a59c860b9dc971c98b/e4a3f/ci_0.png 295w,
/static/7b1f2ba759f6a7a59c860b9dc971c98b/fcda8/ci_0.png 590w,
/static/7b1f2ba759f6a7a59c860b9dc971c98b/efc66/ci_0.png 885w,
/static/7b1f2ba759f6a7a59c860b9dc971c98b/c83ae/ci_0.png 1180w,
/static/7b1f2ba759f6a7a59c860b9dc971c98b/11a8f/ci_0.png 1272w&quot;
        sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If the plan looks good, the PR can be merged.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Once the changes are in the main branch, the plan can be applied(automatically or with a manual trigger).&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This approach has several benefits. The CI will reliably set up Terraform, its providers, and other supporting packages. It can carry out tasks repeatedly without drifting configurations. Engineers can make multiple infra changes can be made in parallel. It is collaborative, as the logs from the Terraform run are visible to people inside your org. People can review the diff, request changes, and suggest changes quickly. Moreover, you can roll back reliably.&lt;/p&gt;
&lt;p&gt;This workflow increases everyone’s participation in infrastructure and builds confidence in changes. And let’s be honest, solving ops problems all the time is boring and soon becomes frustrating. If you can’t avoid it, best automate it. Once you have Terraform running on CI, you can “hopefully” ask dev teams to raise PRs — for simple ops changes. You can point them to how to do it from similar ops changes done in the past. Of course, this claim that developers will raise Infra PR depends heavily on the engineering culture of the org 😅. But I think it brings hope for collaboration, better than zero visibility into infrastructure by developer teams.&lt;/p&gt;
&lt;p&gt;With the intent set, part two of this blog will discuss the technical aspects of setting up Terraform on CI. We will cover Authentication and Authorization with cloud providers, Terraform providers, and CI triggers. Stay tuned!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Deploying OTEL Collector as Daemonset]]></title><description><![CDATA[Running the OpenTelemetry Collector as a Kubernetes DaemonSet at Pixxel to discover targets, scrape metrics and export telemetry.]]></description><link>https://absh.dev/otel-daemon/</link><guid isPermaLink="false">https://absh.dev/otel-daemon/</guid><pubDate>Sat, 02 Dec 2023 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;The open telemetry collector is one of the most popular CNCF projects that attempts to standardise telemetry data collection, processing, and export. At &lt;a href=&quot;https://www.pixxel.space/&quot;&gt;Pixxel&lt;/a&gt;, we use OTEL Collector to discover targets in a k8s cluster, scrape metrics from the configured endpoint and export the metrics to the Grafana Mimir server. This is a mix of both push and pull approaches. The pull part refers to discovering targets and scraping the endpoints. The push part refers to remote-writing the metrics to a Monitoring server.&lt;/p&gt;
&lt;p&gt;An alternative way to achieve this setup is to run Prometheus in &lt;a href=&quot;https://prometheus.io/blog/2021/11/16/agent/&quot;&gt;agent mode&lt;/a&gt; which can scrape targets and remotewrite to desired endpoint. However, a significant drawback is fault tolerance. One Prometheus pod per cluster(agent or normal mode) is a single point of failure. If that pod becomes unhealthy, you may lose metrics for the whole cluster. However, these limitations can be overcome by proper resource/limit configuration to signal the priority of the pod to the Kubernetes cluster or by correct retry/buffer configuration that presents you with more ops work.
Moreover, you may need to revisit these configurations as and when the workload increases in the cluster. It feels more work than a setup that distributes the scraping workload to many replicas(daemon sets). The OTEL collector was a no-brainer choice since Prometheus doesn’t support a daemon set deployment. Although there are other collectors that support daemonset deployment: like Grafana Agent.&lt;/p&gt;
&lt;p&gt;The following section describes configuring the OTEL collector as a daemon set. The fun part is leveraging Kubernetes pod service discovery with a node name filter. Since there weren’t many good tutorials available on this deployment mode, I decided to write one :P&lt;/p&gt;
&lt;h2&gt;Setup&lt;/h2&gt;
&lt;p&gt;Let’s use the OTEL Collector &lt;a href=&quot;https://github.com/open-telemetry/opentelemetry-helm-charts/tree/main/charts/opentelemetry-collector&quot;&gt;helm chart&lt;/a&gt; to deploy it as Daemonset. I’ll be using Minikube for the demonstration below. Let’s start a local Kubernetes cluster with two nodes.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;minikube start &lt;span class=&quot;token parameter variable&quot;&gt;--nodes&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-p&lt;/span&gt; local-k8s-cluster&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Add OpenTelemety repo to Helm:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;helm repo &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Service Discovery&lt;/h2&gt;
&lt;p&gt;We want to scrape only those pods running on the same node for each OTEL collector pod. We can achieve that in 2 steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Discover all pods using &lt;code class=&quot;language-text&quot;&gt;kubernetes_sd_config&lt;/code&gt;. We will need to provide cluster roles to our daemon set to discover pods.&lt;/li&gt;
&lt;li&gt;Filter using &lt;code class=&quot;language-text&quot;&gt;nodeName&lt;/code&gt; spec. &lt;a href=&quot;https://kubernetes.io/docs/tasks/inject-data-application/environment-variable-expose-pod-information/&quot;&gt;ref&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;scrape_configs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;job_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; otel&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;daemon&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;scraping
    &lt;span class=&quot;token key atrule&quot;&gt;scheme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; http
    &lt;span class=&quot;token key atrule&quot;&gt;kubernetes_sd_configs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; pod
        &lt;span class=&quot;token key atrule&quot;&gt;selectors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; pod
            &lt;span class=&quot;token comment&quot;&gt;# only scrape data from pods running on the same node as the collector&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;# assuming KUBE_NODE_NAME env will be set in collector pods env&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;spec.nodeName=${env:KUBE_NODE_NAME}&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;With the nodeName field selector filter, we can pick only targets on the same node. The node name is read from the env variable(assuming it is set in the env of the pods during provisioning). Prometheus doesn’t support env variables in the config file, which limits its usage as a daemonset.&lt;/p&gt;
&lt;p&gt;After the pods have been discovered per node, we need a way to find the port number and path on the pods to scrape metrics. We can leverage pod labels and annotations for this. OTEL Collector provides the pod label and annotations inside the metric label for that target, which we can read and take required action using relabeling(&lt;code class=&quot;language-text&quot;&gt;relabel_configs&lt;/code&gt;):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;relabel_configs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;# scrape pods annotated with &quot;prometheus.io/scrape: true&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;source_labels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;__meta_kubernetes_pod_annotation_prometheus_io_scrape&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;regex&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; keep
  &lt;span class=&quot;token comment&quot;&gt;# read the port from &quot;prometheus.io/port: &amp;lt;port&gt;&quot; annotation and update scraping address accordingly&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;source_labels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;__address__&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; __meta_kubernetes_pod_annotation_prometheus_io_port&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; replace
    &lt;span class=&quot;token key atrule&quot;&gt;target_label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; __address__
    &lt;span class=&quot;token key atrule&quot;&gt;regex&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; (&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;^&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;+)(&lt;span class=&quot;token punctuation&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;\d+)&lt;span class=&quot;token punctuation&quot;&gt;?&lt;/span&gt;;(\d+)
    &lt;span class=&quot;token comment&quot;&gt;# escaped $1:$2&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;replacement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $$1&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;$$2&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The first item in the &lt;code class=&quot;language-text&quot;&gt;relabel_configs&lt;/code&gt; array checks for the presence of the annotation &lt;code class=&quot;language-text&quot;&gt;prometheus.io/scrape: true&lt;/code&gt; on the discovered target. Only those targets that match the regex specified in the &lt;code class=&quot;language-text&quot;&gt;keep&lt;/code&gt; block are kept. &lt;code class=&quot;language-text&quot;&gt;keep&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;drop&lt;/code&gt; actions allow us to filter out targets and metrics based on whether our label values match the provided regex. The second item in the array configures the host and port of the target. The scheme is set to &lt;code class=&quot;language-text&quot;&gt;http&lt;/code&gt; by default. The path is set to &lt;code class=&quot;language-text&quot;&gt;/metrics&lt;/code&gt; by default. These configs can also be overridden by relabelling but we will go with the defaults for brevity.&lt;/p&gt;
&lt;p&gt;So far, we have established the receiver configuration of the OTEL Collector. To complete the demonstration, end to end, I will run a Prometheus Server where our collector pods will write the metrics. We can set up a Grafana instance to query from this server afterwards.&lt;/p&gt;
&lt;h2&gt;Metric storage with Prometheus&lt;/h2&gt;
&lt;p&gt;Here is a Kubernetes manifest to create a Prometheus deployment enabled with remote write, exposed via a K8s Service.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# prometheus.yml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; v1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ConfigMap
&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; prometheus&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;server&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;conf
&lt;span class=&quot;token key atrule&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;prometheus.yml&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;global&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;scrape_interval&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 5s
      &lt;span class=&quot;token key atrule&quot;&gt;evaluation_interval&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 5s
    &lt;span class=&quot;token key atrule&quot;&gt;scrape_configs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; apps/v1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Deployment
&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; prometheus
  &lt;span class=&quot;token key atrule&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; prometheus&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;server
&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;replicas&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;matchLabels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; prometheus&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;server
  &lt;span class=&quot;token key atrule&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; prometheus&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;server
    &lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;containers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; prometheus
          &lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; prom/prometheus
          &lt;span class=&quot;token key atrule&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;--storage.tsdb.retention.time=12h&apos;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;--config.file=/etc/prometheus/prometheus.yml&apos;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;--storage.tsdb.path=/prometheus/&apos;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;--enable-feature=remote-write-receiver&apos;&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;containerPort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;9090&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;volumeMounts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; prometheus&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;volume
              &lt;span class=&quot;token key atrule&quot;&gt;mountPath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; /etc/prometheus/
            &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; prometheus&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;storage&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;volume
              &lt;span class=&quot;token key atrule&quot;&gt;mountPath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; /prometheus/
      &lt;span class=&quot;token key atrule&quot;&gt;volumes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; prometheus&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;volume
          &lt;span class=&quot;token key atrule&quot;&gt;configMap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;defaultMode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;420&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; prometheus&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;server&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;conf
        &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; prometheus&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;storage&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;volume
          &lt;span class=&quot;token key atrule&quot;&gt;emptyDir&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; v1
&lt;span class=&quot;token key atrule&quot;&gt;kind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Service
&lt;span class=&quot;token key atrule&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; prometheus
&lt;span class=&quot;token key atrule&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;selector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; prometheus&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;server
  &lt;span class=&quot;token key atrule&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;protocol&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; TCP
      &lt;span class=&quot;token key atrule&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;9090&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;targetPort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;9090&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Apply the above helm override to our cluster.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;kubectl apply &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; prometheus.yml&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;After applying the above file to our cluster we will have a working Prometheus deployment ready to accept remote writes. It is exposed via a kubernetes service which can be accessed via this DNS:&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;http://prometheus.default.svc.cluster.local:9090&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;Collector config&lt;/h3&gt;
&lt;p&gt;Now is the time to push some data to Prometheus. Let’s deploy our OTEL collector with the proper configuration using the Helm override file below.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# otel-override.yaml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;daemonset&apos;&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;extraEnvs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; KUBE_NODE_NAME
    &lt;span class=&quot;token key atrule&quot;&gt;valueFrom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;fieldRef&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;fieldPath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; spec.nodeName

&lt;span class=&quot;token key atrule&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;exporters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;logging&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;verbosity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; detailed
    &lt;span class=&quot;token key atrule&quot;&gt;prometheusremotewrite&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;endpoint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; http&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;//prometheus.default.svc.cluster.local&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;9090/api/v1/write
      &lt;span class=&quot;token key atrule&quot;&gt;external_labels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;collector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; otel&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;collector

  &lt;span class=&quot;token key atrule&quot;&gt;receivers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;prometheus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;scrape_configs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;job_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; metrics&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;exporter
            &lt;span class=&quot;token key atrule&quot;&gt;scheme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; http
            &lt;span class=&quot;token key atrule&quot;&gt;kubernetes_sd_configs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; pod
                &lt;span class=&quot;token key atrule&quot;&gt;selectors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; pod
                    &lt;span class=&quot;token comment&quot;&gt;# # only scrape data from pods running on the same node as prometheus&lt;/span&gt;
                    &lt;span class=&quot;token key atrule&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;spec.nodeName=${env:KUBE_NODE_NAME}&apos;&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;relabel_configs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;token comment&quot;&gt;# scrape pods annotated with &quot;prometheus.io/scrape: true&quot;&lt;/span&gt;
              &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;source_labels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;__meta_kubernetes_pod_annotation_prometheus_io_scrape&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
                &lt;span class=&quot;token key atrule&quot;&gt;regex&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
                &lt;span class=&quot;token key atrule&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; keep
              &lt;span class=&quot;token comment&quot;&gt;# read the port from &quot;prometheus.io/port: &amp;lt;port&gt;&quot; annotation and update scraping address accordingly&lt;/span&gt;
              &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;source_labels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
                  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
                    __address__&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                    __meta_kubernetes_pod_annotation_prometheus_io_port
                  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
                &lt;span class=&quot;token key atrule&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; replace
                &lt;span class=&quot;token key atrule&quot;&gt;target_label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; __address__
                &lt;span class=&quot;token key atrule&quot;&gt;regex&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; (&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;^&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;+)(&lt;span class=&quot;token punctuation&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;\d+)&lt;span class=&quot;token punctuation&quot;&gt;?&lt;/span&gt;;(\d+)
                &lt;span class=&quot;token comment&quot;&gt;# escaped $1:$2&lt;/span&gt;
                &lt;span class=&quot;token key atrule&quot;&gt;replacement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; $$1&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;$$2

  &lt;span class=&quot;token key atrule&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;pipelines&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;metrics&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;receivers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;prometheus&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;exporters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;prometheusremotewrite&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;clusterRole&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;rules&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;apiGroups&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; pods
      &lt;span class=&quot;token key atrule&quot;&gt;verbs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;get&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;list&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;watch&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;

  &lt;span class=&quot;token key atrule&quot;&gt;clusterRoleBinding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;otel-discoverer&apos;&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;otlp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;false&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;otlp-http&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;false&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;jaeger-compact&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;false&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;jaeger-thrift&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;false&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;jaeger-grpc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;false&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;zipkin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;false&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;metrics&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;false&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This can be applied to cluster: &lt;code class=&quot;language-text&quot;&gt;helm install otel-collector open-telemetry/opentelemetry-collector -f otel-override.yaml&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Let’s go through some important aspects of this Helm override:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;We set the deployment to daemon set using &lt;code class=&quot;language-text&quot;&gt;mode&lt;/code&gt; and added an extra env var to capture the node name on each daemon pod of OTEL Collector.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The metrics pipeline is built using the &lt;code class=&quot;language-text&quot;&gt;prometheus&lt;/code&gt; receiver and &lt;code class=&quot;language-text&quot;&gt;prometheusremotewrite&lt;/code&gt; exporter defined in their respective sections. If you are unfamiliar with receivers and exporters, here is a &lt;a href=&quot;https://opentelemetry.io/docs/collector/configuration/&quot;&gt;reference&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The receiver has rules for pod discovery using pod annotations. It picks only those pods that are running on the same node using the nodeName field selector. The nodeName is specified in a templated way, &lt;code class=&quot;language-text&quot;&gt;spec.nodeName=${env:KUBE_NODE_NAME}&lt;/code&gt;, which will render to node name during runtime.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The exporter writes to the Prometheus server at its remote write endpoint. It also places a label &lt;code class=&quot;language-text&quot;&gt;collector: otel-collector&lt;/code&gt; in all the series. Such “global” labels can be crucial when running multiple Kubernetes clusters in the same/different cloud accounts(AWS Account/GCP project).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The required cluster role and binding are specified to discover pods.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;We also don’t open the OTEL Container Ports that are not needed in the scope of this article.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Node Exporter workload&lt;/h2&gt;
&lt;p&gt;Let’s run some actual workload and scrape metrics from it. &lt;a href=&quot;https://github.com/prometheus/node_exporter&quot;&gt;Node Exporter&lt;/a&gt; is an utility that exposes machine metrics. For our demonstration, we will run this inside Kubernetes cluster as deamonset. This will ensure there is one pod per minikube node. These pods can be scraped with our OTEL Collector pods.&lt;/p&gt;
&lt;p&gt;We will install Node Exporter using the helm chart with all defailt with few additions. The pod annotations will be added for discovering these pods. Since NodeExporter by default exposes the metrics in &lt;code class=&quot;language-text&quot;&gt;http&lt;/code&gt; scheme and &lt;code class=&quot;language-text&quot;&gt;/metrics&lt;/code&gt; path, we won’t need to specify it explicitly as our collectors already know that.&lt;/p&gt;
&lt;p&gt;Let’s first add the repo:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;helm repo &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; prometheus-community https://prometheus-community.github.io/helm-charts&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For scraping the node exporter pod, we require proper pod annotation that can be specified using the helm override file.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# node-exporter-override.yml&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;podAnnotations&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;prometheus.io/scrape&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;true&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;prometheus.io/port&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;9100&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Install NodeExporter using helm with above mentioned override file:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;helm &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; node-exporter prometheus-community/prometheus-node-exporter &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; node-exporter-override.yml&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;With all components(Node Exporter daemon, OTEL Collector daemon and prometheus pod) up and running, this is how the cluster should look like:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;➜  kubectl get pods
NAME                                                 READY   STATUS    RESTARTS   AGE
node-exporter-prometheus-node-exporter-h2vxr         &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;/1     Running   &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;          7m
node-exporter-prometheus-node-exporter-kcds4         &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;/1     Running   &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;          7m
otel-collector-opentelemetry-collector-agent-9rxm7   &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;/1     Running   &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;          5m1s
otel-collector-opentelemetry-collector-agent-mbq6x   &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;/1     Running   &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;          5m1s
prometheus-5b54c7c696-4kzjj                          &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;/1     Running   &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;          6m4h&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Querying data&lt;/h3&gt;
&lt;p&gt;If everything goes right, you should see the data from node_exporter daemons in Prometheus. The simplest way to query is to pop up the prometheus UI on your browser and query for some data.&lt;/p&gt;
&lt;p&gt;You can access the Prometheus UI on your browser by port-forwarding the Prometheus Kubernetes service (make sure your port 9090 on your local machine is free to use)&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;kubectl port-forward svc/prometheus &lt;span class=&quot;token number&quot;&gt;9090&lt;/span&gt;:9090&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Navigate to &lt;a href=&quot;http://localhost:9090&quot;&gt;http://localhost:9090&lt;/a&gt; and execute the query shown in the image below. Verify if you are getting a one-time series row per node_exporter.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/72dcdf7257021b95801e15b541dafc6b/06e6b/query.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 49.32432432432432%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAABTklEQVR42o2R207CQBCG96FUEBQwGuKBxGve0AfxEbwyJkI5LAUJ0pbubne3vzPFJkWqcZIvf+fQ2dkdcTt4RPe6j9blFRrtHs5aXZyeMx2cEA3ym23ymxdHNFsd9G76uOo/4O5+gOFwCPE2nmEcTBBMJnifLhDMJGmIqVwimK/IX0CGIRbh8ghJzGVISLy8jvD0PIZYrTdFks2YDEqpgtlcFljr8B8z1mO0ziCiKMLmc0vNDNI0RbJLsSOWqw+EhNIaWZYVeab8Zq1i7R7h3H6CPM8PtM5+rSE/975oLBSfTE21tQdax181HFPaQGSZhacTHJ1QqiP1NVRr6nKGJ+Qx+QqeAqX+JP+mrCnr+LmqaHpvoY3GdhuBl8Max3GFpIjHcYQkSRCXUI5/5iaWrso4Z/dLsRTk7aapwo6UC6vbZP84ZooJq+ZpT9rl+AIg5OBZpRjMmwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;PromQuery&quot;
        title=&quot;&quot;
        src=&quot;/static/72dcdf7257021b95801e15b541dafc6b/fcda8/query.png&quot;
        srcset=&quot;/static/72dcdf7257021b95801e15b541dafc6b/12f09/query.png 148w,
/static/72dcdf7257021b95801e15b541dafc6b/e4a3f/query.png 295w,
/static/72dcdf7257021b95801e15b541dafc6b/fcda8/query.png 590w,
/static/72dcdf7257021b95801e15b541dafc6b/efc66/query.png 885w,
/static/72dcdf7257021b95801e15b541dafc6b/c83ae/query.png 1180w,
/static/72dcdf7257021b95801e15b541dafc6b/06e6b/query.png 3809w&quot;
        sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;We can also spin up a Grafana instance for better visualisation: Port-forward Prometheus service locally and add the localhost:port as Data Source(also called Connections) in this Grafana instance.&lt;/p&gt;
&lt;h3&gt;Debugging&lt;/h3&gt;
&lt;p&gt;If something doesn’t work as expected, you can use debug logs to see the errors.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;OTEL collector debug log: Modify the “service” section to add additional debug config.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;service&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;telemetry&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;logs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;debug&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;pipelines&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;metrics&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;receivers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;prometheus&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;exporters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;prometheusremotewrite&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Prometheus debug logs can be enabled via the flag &lt;code class=&quot;language-text&quot;&gt;&quot;--log.level=debug&quot;&lt;/code&gt; in the &lt;code class=&quot;language-text&quot;&gt;spec.containers.arg&lt;/code&gt; list&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;p&gt;To recap, we looked into how to deploy OTEL collectors as daemons on a Kubernetes cluster and discover pods local to the collector’s node. We also looked into how to scrape metrics from the discovered pods and export these metrics to a remote server. We ran node exporter pods and scraped metrics from them. We also visualised the metrics in Prometheus UI. In the end, we looked into how to debug in case things don’t work as expected. That’s all I had to document for OTEL Collector usage. Stay tuned for more such blogs!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[cAdvisor high cardinality]]></title><description><![CDATA[How cAdvisor metrics blow up Prometheus cardinality in Kubernetes — and how to find and cut the expensive labels.]]></description><link>https://absh.dev/cadvisor-metrics-cardinality/</link><guid isPermaLink="false">https://absh.dev/cadvisor-metrics-cardinality/</guid><pubDate>Sat, 18 Nov 2023 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;cAdvisor is a popular utility that provides resource usage and performance characteristics of running containers. It collects, aggregates and exports metrics about running containers. It comes integrated inside the Kubelet binary inside Kubernetes clusters. We can collect the cAdvisor metrics from the Kubelet API Endpoint &lt;code class=&quot;language-text&quot;&gt;/metrics/cadvisor&lt;/code&gt;. cAdvisor metrics can be beneficial to look at the resource consumption of your workload to achieve optimal resource utilisation, debug issues with containers and usage trends. These metrics are represented in the Prometheus Exposition format.&lt;/p&gt;
&lt;p&gt;Here is an example metric for CPU Utilisation:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;# HELP container_cpu_system_seconds_total Cumulative system cpu time consumed in seconds.
# TYPE container_cpu_system_seconds_total counter
container_cpu_system_seconds_total{container=&quot;&quot;,id=&quot;/kubepods.slice/kubepods-besteffort.slice/kubepods-besteffort-pod05b3ee07_a2bd_47e7_9599_7961201f20dd.slice&quot;,image=&quot;&quot;,name=&quot;&quot;,namespace=&quot;test&quot;,pod=&quot;test-workload-7756958c5b-qtkgc&quot;} 22.65 1699453605805&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Since cAdvisor or Kubelet doesn’t store these metrics, one can not query historical data, which means users should periodically scrape these metrics and store them in some observability server like Prometheus or Grafana Mimir, etc.&lt;/p&gt;
&lt;h2&gt;Scraping cAdvisor&lt;/h2&gt;
&lt;p&gt;Since one cAdvisor instance is running per node, we will need to scrape all of them to get the complete picture of the resource utilisation. At Pixxel, we deploy Open Telemetry collectors as Daemonset to scrape metrics from pods running on all the nodes. That means each node will have one OTEL collector pod scraping the cAdvisor metrics(along with other endpoints).&lt;/p&gt;
&lt;p&gt;The following snippet describes how you can scrape cAdvisor metrics using OTEL pods:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;receivers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;prometheus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;scrape_configs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;job_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; cadvisor
          &lt;span class=&quot;token key atrule&quot;&gt;scheme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; https
          &lt;span class=&quot;token key atrule&quot;&gt;tls_config&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;ca_file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
            &lt;span class=&quot;token key atrule&quot;&gt;insecure_skip_verify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;bearer_token_file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; /var/run/secrets/kubernetes.io/serviceaccount/token
          &lt;span class=&quot;token key atrule&quot;&gt;kubernetes_sd_configs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;role&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; node
          &lt;span class=&quot;token key atrule&quot;&gt;relabel_configs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; labelmap
              &lt;span class=&quot;token key atrule&quot;&gt;regex&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; __meta_kubernetes_node_label_(.+)
            &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;target_label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; __address__
              &lt;span class=&quot;token key atrule&quot;&gt;replacement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; kubernetes.default.svc.cluster.local&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;443&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;source_labels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;__meta_kubernetes_node_name&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
              &lt;span class=&quot;token key atrule&quot;&gt;regex&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; (.+)
              &lt;span class=&quot;token key atrule&quot;&gt;target_label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; __metrics_path__
              &lt;span class=&quot;token key atrule&quot;&gt;replacement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; /api/v1/nodes/$$$&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;/proxy/metrics/cadvisor&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The above should look familiar if you have experience configuring Prometheus. OTEL collector can be a drop-in replacement for Prometheus for scraping with some added benefits - like &lt;a href=&quot;https://opentelemetry.io/docs/collector/configuration/#configuration-environment-variables&quot;&gt;env variable support&lt;/a&gt;. We are scraping the cAdvisor target by doing Kubernetes Node Service discovery. The &lt;code class=&quot;language-text&quot;&gt;kubernetes_sd_configs&lt;/code&gt; retrievs scrape targets from Kubernetes REST API. The &lt;code class=&quot;language-text&quot;&gt;role: node&lt;/code&gt; discovers one target per cluster node with the address defaulting to the Kubelet’s HTTP port. We are explicitly setting the address(host and port) and path of the scrape target to the local API Server. I’ll quote Kubernetes documentation on how this address is formed:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The API server’s in-cluster address is also published to a Service named kubernetes in the default namespace so that pods may reference kubernetes.default.svc as a DNS name for the local API server.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Pods running in the cluster should authenticate with the API server with service account credentials. An equivalent curl request would be:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-k&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Authorization: Bearer &lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cat&lt;/span&gt; /var/run/secrets/kubernetes.io/serviceaccount/token&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;https://kubernetes.default.svc.cluster.local:443/api/v1/nodes/ip-10-1-81-147.us-east-2.compute.internal/proxy/metrics/cadvisor&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;https://kubernetes.default.svc.cluster.local/api/v1/nodes/ip-10-1-4-5.us-west-1.compute.internal/proxy/metrics/cadvisor&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;PS: Kubernetes also provides a handy kubectl command to access the API Server, using which we can query cAdvisor metrics:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;kubectl get --raw /api/v1/nodes/ip-10-1-4-5.us-west-1.compute.internal/proxy/metrics/cadvisor&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Metrics Cardinality&lt;/h2&gt;
&lt;p&gt;The metrics prepared by cAdvisor sometimes exceed the label limit set by the observability server. For example, in Grafana Mimir, we see the max label for any series set to 30 (by default) via the config option: &lt;code class=&quot;language-text&quot;&gt;max_label_names_per_series&lt;/code&gt; &lt;a href=&quot;https://grafana.com/docs/mimir/latest/references/configuration-parameters/#limits&quot;&gt;ref&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;After observing OTEL Collector logs after a few hours of cAdvisor scrape setup, I found the Mimir server rejected multiple series because they had more labels than the acceptable limit. Some had 32 and 35. I wanted to see what labels were getting published in cAdvisor. For &lt;code class=&quot;language-text&quot;&gt;container_oom_events_total&lt;/code&gt; metric, I saw the following labels:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;beta_kubernetes_io_arch
beta_kubernetes_io_instance_type
beta_kubernetes_io_os
eks_amazonaws_com_capacityType
eks_amazonaws_com_nodegroup
eks_amazonaws_com_nodegroup_image
eks_amazonaws_com_sourceLaunchTemplateld
eks_amazonaws_com_sourceLaunchTemplateVersion
failure_domain_beta_kubernetes_io_region
failure_domain_beta_kubernetes_io_zone
id
k8s_io_cloud_provider_aws
kubernetes_io_arch
kubernetes_io_hostname
kubernetes_io_os
node_kubernetes_io_instance_type
topology_ebs_csi_aws_com_zone
topology_kubernetes_io_region
topology_kubernetes_io_zone&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;cAdviosr added these 20 labels(and more) in every series in addition to the global demographic labels such as cluster name, AWS account, etc that we add explicitly. In some metrics, this count reached 25 or so. We quickly concluded that not all of these are required - so instead of changing server limits, we could delete these labels without harming anything. Luckily, Prometheus provides a way to rewrite labelset using &lt;code class=&quot;language-text&quot;&gt;metric_relabel_config&lt;/code&gt;. We decided to drop all &lt;code class=&quot;language-text&quot;&gt;eks_&lt;/code&gt; prefixed labels since these value can be derived from hostname alone. The way to do that is:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;relabel_configs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 
  &lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;metric_relabel_configs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; labeldrop
    &lt;span class=&quot;token key atrule&quot;&gt;regex&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;eks.*&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This change brought down the label count to acceptable limits. Practically we may not need even the &lt;code class=&quot;language-text&quot;&gt;topology_&lt;/code&gt; prefixed labels. But we did the least amount of label rewriting to get the system running. If needed in future, we can look into further reducing the labelset using the same approach.&lt;/p&gt;
&lt;h4&gt;References:&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://grafana.com/blog/2022/03/21/how-relabeling-in-prometheus-works/&quot;&gt;Metric Relabeling&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Handling shutdowns, gracefully]]></title><description><![CDATA[How Unix signals work and how to handle them for graceful shutdowns in long-running services.]]></description><link>https://absh.dev/signal-handling/</link><guid isPermaLink="false">https://absh.dev/signal-handling/</guid><pubDate>Fri, 29 Sep 2023 22:13:03 GMT</pubDate><content:encoded>&lt;h2&gt;Signals&lt;/h2&gt;
&lt;p&gt;In operating systems, signals are the messages OS sends to the programs to notify about specific events. They are commonly used to interrupt or kill a process. Linux supports multiple signals, each signifying some event. For example, when you press &lt;code class=&quot;language-text&quot;&gt;CTRL+C&lt;/code&gt; on a process in your terminal, a &lt;code class=&quot;language-text&quot;&gt;SIGINT&lt;/code&gt; signal is sent to the process by OS. It’s an interrupt signal to terminate the program. A more strong shutdown signal is &lt;code class=&quot;language-text&quot;&gt;SIGTERM&lt;/code&gt;. The most forceful of all signals is &lt;code class=&quot;language-text&quot;&gt;SIGKILL&lt;/code&gt;, an instruction to kill the program immediately. You can also send the same signals using the &lt;code class=&quot;language-text&quot;&gt;kill&lt;/code&gt; utility in Linux. This utility is useful when you cannot &lt;code class=&quot;language-text&quot;&gt;CTRL+C&lt;/code&gt; a process, like a daemon. Example command: &lt;code class=&quot;language-text&quot;&gt;kill -SIGINT pid&lt;/code&gt; where &lt;code class=&quot;language-text&quot;&gt;pid&lt;/code&gt; represents the Process ID.&lt;/p&gt;
&lt;p&gt;Modern container orchestration applications like Docker and Kubernetes use signals widely. In the Kubernetes cluster, a user can submit a request to terminate any pod at any time. Kubelet sends the shutdown request to container runtime to stop the containers in the pod. The main process inside each container should handle this signal for a graceful termination. This signal also contains a grace timeout period and defaults to 30 seconds. Read &lt;a href=&quot;https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-termination&quot;&gt;this&lt;/a&gt; for more details on how Kubernetes handles pod termination requests made by users.&lt;/p&gt;
&lt;h3&gt;Importance of handling signals&lt;/h3&gt;
&lt;p&gt;For a graceful shutdown, the program developer should ideally handle the SIGINT or SIGTERM signals manually. Typically, this would mean freeing up resource use by the process, committing WALs, performing cleanup, etc. The steps performed in the graceful shutdown differ with the application. If the program fails to terminate with &lt;code class=&quot;language-text&quot;&gt;SIGTERM&lt;/code&gt;, a more forceful &lt;code class=&quot;language-text&quot;&gt;SIGKILL&lt;/code&gt; might come next and immediately terminate it. Depending on the type of application, this may cause data losses, partial or complete unavailability, deadlocks or inconsistency in metadata, which might fail to reboot the process the next time. I’ve encountered the unavailability and deadlock problems more prevalent in applications that run in some cluster(quorum) and fail to handle graceful shutdown.&lt;/p&gt;
&lt;p&gt;For instance, Consider a scenario where we are running a cluster of 3 nodes. There is a Master node that keeps the status of the cluster, including the two secondary nodes. Suppose a secondary node terminates in the cluster and fails to inform the Primary node about it(i.e. ungraceful shutdown). In that case, the Primary node might stop taking write requests altogether in some configuration. One such configuration could be the cluster running in synchronous replication mode. For any Write request to be successful, all secondary nodes must acknowledge the Primary node that the Write was accepted. Now consider the case where a secondary node dies ungracefully without informing the Primary node. The Primary node will wait for an ack from both nodes for incoming Write requests. Still, one ack never arrives within the timeout, so the primary node rejects the Write requests, resulting in partial unavailability. The primary node can continue serving the read requests; hence, it’s not 100% unavailability. One can handle such scenarios in many ways - like changing the replication from synchronous to asynchronous or allowing the Primary node to poll for status, etc. We can also argue if the secondary node had sent the termination notice to the primary node, the primary node would have better knowledge of the cluster status and stop taking write requests altogether (if it wants to maintain a quorum) or reduce the quorum size to 2 nodes.&lt;/p&gt;
&lt;h4&gt;Golang example&lt;/h4&gt;
&lt;p&gt;We’ve seen the importance of explicitly handling signals to perform cleanup. The following section gives a demonstration of how to handle Signals in Golang. Golang has built-in signal handling packages &lt;a href=&quot;https://pkg.go.dev/os/signal&quot;&gt;os/signal&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;There are two parts to handle graceful shutdown:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Notifying our program for the signal received&lt;/li&gt;
&lt;li&gt;Performing cleanup/shutdown tasks&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For step (1): Golang runtime can notify the program for signals received on a signal channel.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;go&quot;&gt;&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;os/signal&quot;&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;

signalChannel &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;make&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;chan&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Signal&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
signal&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Notify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;signalChannel&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For step (2): You can listen for signals on this newly created channel forever and handle Signals in a separate function.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;go&quot;&gt;&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;os&quot;&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;os/signal&quot;&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;syscall&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// example handler function that ignores &lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// all signals except SIGTERM&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;signal os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Signal&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; signal &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; syscall&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SIGTERM &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;   &lt;span class=&quot;token comment&quot;&gt;//SIGTERM received&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;//primaryNode.notifysecondaryTermination(secondaryNodeId)&lt;/span&gt;
		os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Exit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    signalChannel &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;make&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;chan&lt;/span&gt; os&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Signal&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    signal&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Notify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;signalChannel&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;go&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			s &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt;signalChannel
			&lt;span class=&quot;token function&quot;&gt;handleSignal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To conclude, crafting good software means giving equal importance to failure scenarios. You can build resilience in your program if you care about the failure modes of your program and what happens after a failure is encountered. Signal handling is one such case developers should care about. That’s all for this blog - catch you in the next one. Stay tuned!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Architecting with AWS VPC]]></title><description><![CDATA[A hands-on tour of designing networks on AWS — subnets, route tables, gateways and the decisions behind a production-grade VPC.]]></description><link>https://absh.dev/aws-vpc/</link><guid isPermaLink="false">https://absh.dev/aws-vpc/</guid><pubDate>Fri, 29 Sep 2023 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;Computer networking is a fascinating concept. Networking is how you connect computers worldwide to let them communicate with each other. The computers talk to each other as per the networking rules applied to them. This article will take you through the administration of computer networks in AWS Cloud.&lt;/p&gt;
&lt;h2&gt;VPC&lt;/h2&gt;
&lt;p&gt;A Virtual Proud Cloud(VPC) is an isolated network you can create in your AWS account, similar to a physical computer network (like LAN, etc.). It is logically isolated from other virtual networks in the AWS Cloud.&lt;/p&gt;
&lt;p&gt;There are three essential aspects to any VPC:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Name of the VPC&lt;/li&gt;
&lt;li&gt;Region: A VPC spans all Availability zones within the region.&lt;/li&gt;
&lt;li&gt;Size: A continuous block of IP address represented using CIDR notation. CIDR notation is a mathematical way to represent blocks of IP addresses. &lt;a href=&quot;https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing#CIDR_blocks&quot;&gt;CIDR Ranges&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;With these three inputs, AWS will create a network of IP addresses. The network is a virtual boundary where AWS will deploy your resources.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/8b39cb61854c38383cfb83aac36b1a96/network.svg&quot; alt=&quot;Network&quot;&gt;&lt;/p&gt;
&lt;p&gt;By default, all these IP addresses are private, which means no one can access the resources from outside the network, but resources inside the boundary can access each other. The first usable IP address in this range is 192.168.0.1. We will look into architecting the VPC to create network topologies of our choice.&lt;/p&gt;
&lt;p&gt;To allow internet connectivity to your VPC, you must create an internet gateway(IGw hereon). It’s a highly available and scalable utility that allows traffic from the Internet to reach resources in your VPC. Each IGw has a unique ID in AWS.&lt;/p&gt;
&lt;h3&gt;Subnet&lt;/h3&gt;
&lt;p&gt;A subnet is a network inside the VPC. Think of it like a chunk of the “VPC pie” shown above. It is a continuous block of networks contained inside the VPC.&lt;/p&gt;
&lt;p&gt;For example, 192.168.0.1/26 represents a total of 64 IP addresses in the (usable)range 192.168.0.1 to 192.168.0.62&lt;/p&gt;
&lt;p&gt;We use subnets to isolate and optimize network traffic. We can also use them to provide high availability and connectivity options for the resources. Specific privacy rules decide what traffic can get in and out of any subnet. We represent these rules as “Route Tables”.&lt;/p&gt;
&lt;p&gt;In AWS, any subnet has three significant aspects:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The VPC it belongs to&lt;/li&gt;
&lt;li&gt;The availability zone&lt;/li&gt;
&lt;li&gt;CIDR block&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Route Tables&lt;/h2&gt;
&lt;p&gt;External traffic reaches inside the VPC via an IGw. But for the traffic to get to the right resource within VPC, we must route it to the correct network(Subnet). That’s where Route tables come into the picture. Route tables decide the routing of traffic within a VPC. For example, to allow traffic from IGw to a subnet, a route must exist from the IGw to the Subnet.&lt;/p&gt;
&lt;p&gt;We classify a subnet as public or private by the type of routing table linked to it. We can apply Route tables at the subnet level or VPC level. Hence, we should place the resources that need Internet connectivity inside a public subnet. Similarly, put all resources that don’t need Internet connectivity inside a private subnet.&lt;/p&gt;
&lt;p&gt;Here is an example route table:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Destination&lt;/th&gt;
&lt;th&gt;Target&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0.0.0.0/0&lt;/td&gt;
&lt;td&gt;igw-id&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10.1.0.0/16&lt;/td&gt;
&lt;td&gt;local&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Any route table has no meaning unless it gets attached to a network (i.e. Subnet). Each row in this table is a route that determines where to direct traffic. The first column represents the source of the traffic, and the second column represents the medium.&lt;/p&gt;
&lt;p&gt;So, inside the table shown above, the first row implies the following :&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;0.0.0.0/0&lt;/code&gt; represent traffic from any source (meaning all IP address in the universe) via the internet gateway and should be routed inside the Subnet this route table is attached to.&lt;/p&gt;
&lt;p&gt;The second row implies that only traffic in the &lt;code class=&quot;language-text&quot;&gt;10.1.0.0/16&lt;/code&gt; range should be routed to the Subnet this route table is attached to. &lt;strong&gt;local&lt;/strong&gt; is a particular target that means traffic from other subnets within the same VPC.&lt;/p&gt;
&lt;p&gt;AWS route tables support many targets such as NAT Gateway, Network Interface, Peering connection, outpost local gateway, virtual private gateway, etc.&lt;/p&gt;
&lt;p&gt;Each VPC has a default route table, the &lt;strong&gt;Main Route Table&lt;/strong&gt; attached to it. The following describes the content of the Main Route table. For our VPC shown in the image above, the Main Route Table will contain the following two rows:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Destination&lt;/th&gt;
&lt;th&gt;Target&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;192.168.0.1/24&lt;/td&gt;
&lt;td&gt;local&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0.0.0.0/0&lt;/td&gt;
&lt;td&gt;igw-id&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;AWS assumes you want traffic to move between your resources created in the VPC, making the first entry by default. The second entry specifies traffic from the Internet can reach the VPC.&lt;/p&gt;
&lt;h2&gt;Architecting VPCs&lt;/h2&gt;
&lt;p&gt;AWS creates VPC for us in every region by default. When you create a new AWS account, you get a default VPC. Default VPCs provide a way to access the EC2 instances over the Internet properly. For your usage, you can select the default VPC or build a custom VPC on your own. The custom VPC could be more secure and provide granular options as you can control the subnets’ configuration and route tables.&lt;/p&gt;
&lt;p&gt;A default VPC has a public subnet in each Availability Zone, an internet gateway, and settings to enable DNS resolution. Therefore, you can immediately launch Amazon EC2 instances into a default VPC - designed to fast-track the simple use cases. The default VPC provisions the CIDR range, subnets and gateway for you. The following is a list of configuration AWS sets for your default VPC:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Creates VPC of size &lt;code class=&quot;language-text&quot;&gt;/16&lt;/code&gt; IPv4 CIDR block (&lt;code class=&quot;language-text&quot;&gt;172.31.0.0/16&lt;/code&gt;). This block provides up to 65,536 private IPv4 addresses.&lt;/li&gt;
&lt;li&gt;Creates default subnet of size &lt;code class=&quot;language-text&quot;&gt;/20&lt;/code&gt; in each Availability Zone. This Subnet provides up to 4,096 addresses per Subnet. AWS reserves a few of them.&lt;/li&gt;
&lt;li&gt;Creates an Internet gateway and connects it to default VPC - so your VPC has Internet access by default.&lt;/li&gt;
&lt;li&gt;Adds a route to the main route table that points all traffic (&lt;code class=&quot;language-text&quot;&gt;0.0.0.0/0&lt;/code&gt;) to the internet gateway.&lt;/li&gt;
&lt;li&gt;Adds a route to the main route that allows inter-subnet traffic&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once you create the VPC, you should create subnets that let you decide the visibility of the IP addresses to the outside world. In the following section, we will deploy a sample application to see all the components in action.&lt;/p&gt;
&lt;h2&gt;AWS Networking in action&lt;/h2&gt;
&lt;p&gt;Our application is a movie directory consisting of a frontend that shows a catalogue of movies in a web-based interface and a database with some movies stored along with relevant tags and metadata(such as director name, release year, genre, etc.). We want to deploy the frontend and backend on separate EC2 machines where only the frontend should be accessible from the public Internet. Let’s begin with a simple organization of resources inside AWS VPC.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/0604feb51beacfbf7bccc97a88696233/vpc.svg&quot; alt=&quot;VPC&quot;&gt;&lt;/p&gt;
&lt;p&gt;At the topmost level - we have the US-east-2 region represented with two availability zones. Let’s go through what we’ve done in the above diagram:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We created a VPC denoted by &lt;code class=&quot;language-text&quot;&gt;10.1.0.0/16&lt;/code&gt; and added an Internet Gateway to it&lt;/li&gt;
&lt;li&gt;The VPC is split into four equal-sized subnets. Each availability zone will have two subnets, one public and one private. The Public subnets will have a custom route table associated, allowing a route from &lt;code class=&quot;language-text&quot;&gt;0.0.0.0/0&lt;/code&gt; via the Internet Gateway. You should place resources that must be reachable from outside the VPC inside this Subnet.&lt;/li&gt;
&lt;li&gt;We host two replicas of each instance(frontend, backend and database) for high availability. We disperse the replicas of each application in different availability zones. So, if an availability zone goes down, our application remains reachable to users or other services.&lt;/li&gt;
&lt;li&gt;We want only frontend replicas to be reachable from the Internet. Hence, we put frontend replicas inside Public Subnets. We put Backend and Database replicas inside a Private subnet. Frontend replicas can reach resources in the private Subnet with the help of route table entry &lt;code class=&quot;language-text&quot;&gt;10.1.0.0/16&lt;/code&gt; via the &lt;code class=&quot;language-text&quot;&gt;local&lt;/code&gt; target.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That’s all for a basic introduction to AWS Networking. We will explore more AWS-related concepts in future blogs. Stay tuned!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Siren - Alert management at scale]]></title><description><![CDATA[Building Siren, an alert orchestration platform at Gojek — managing alerting rules and notifications at scale.]]></description><link>https://absh.dev/siren/</link><guid isPermaLink="false">https://absh.dev/siren/</guid><pubDate>Sun, 16 Jul 2023 22:14:03 GMT</pubDate><content:encoded>&lt;p&gt;This blog takes inspiration from a significant project I undertook at Gojek in 2021-22. It has been pending publication for a while, mainly due to my lack of diligence 🙈. However, I have now resolved to be more active in blogging, starting with the unpublished drafts I have accumulated. Without further ado, let’s dive into it.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Siren is an orchestrator for alerting, written in Golang and open-sourced under &lt;a href=&quot;https://github.com/raystack/siren&quot;&gt;Raystack&lt;/a&gt; community developed at Gojek Data Platform team back in 2021.&lt;/p&gt;
&lt;p&gt;The Gojek Data platform team provides various offerings to Gojek’s engineering teams in order to make their data journey hassle-free and self-serve. The team enables engineers to ingest, process, operate and consume data with just a few clicks of buttons. This is achieved via a suite of products, serving use cases in the data lifecycle viz: ingestion, loading, transformation, intelligence, analytics, and so on.&lt;/p&gt;
&lt;p&gt;A snapshot of range of products that build the data-experience:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4a6941b77de7e4d6f55dbfe0b20975ea/eea4a/raystack.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 47.2972972972973%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAJABQDASIAAhEBAxEB/8QAFgABAQEAAAAAAAAAAAAAAAAAAQAF/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAwDAQACEAMQAAAB3ZBmP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEAAQUCX//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABQQAQAAAAAAAAAAAAAAAAAAACD/2gAIAQEABj8CX//EABkQAAIDAQAAAAAAAAAAAAAAAABBARCB0f/aAAgBAQABPyHWayCUdr//2gAMAwEAAgADAAAAEPvP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAHBAAAgICAwAAAAAAAAAAAAAAAREAITFREHGB/9oACAEBAAE/EARTIWVZjDDLxMSy71M+01w//9k=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Raystack offering&quot;
        title=&quot;&quot;
        src=&quot;/static/4a6941b77de7e4d6f55dbfe0b20975ea/1c72d/raystack.jpg&quot;
        srcset=&quot;/static/4a6941b77de7e4d6f55dbfe0b20975ea/a80bd/raystack.jpg 148w,
/static/4a6941b77de7e4d6f55dbfe0b20975ea/1c91a/raystack.jpg 295w,
/static/4a6941b77de7e4d6f55dbfe0b20975ea/1c72d/raystack.jpg 590w,
/static/4a6941b77de7e4d6f55dbfe0b20975ea/a8a14/raystack.jpg 885w,
/static/4a6941b77de7e4d6f55dbfe0b20975ea/fbd2c/raystack.jpg 1180w,
/static/4a6941b77de7e4d6f55dbfe0b20975ea/eea4a/raystack.jpg 1280w&quot;
        sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Among the various products available for supporting these use cases, Siren stands out in the operations domain. Siren enables developers to monitor the well-being of these products by placing alerts on their data pipelines. Now, let’s delve into the process of accomplishing this.&lt;/p&gt;
&lt;h2&gt;Introduction &lt;/h2&gt;
&lt;p&gt;Alerts are integral part of any observability pipeline. Once the automated system detects any anomaly in the system, it can trigger an alert, informing humans about it. Alerts can be defined on the metrics that developers care about like these scenarios below:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A Kafka broker is degraded e.g. CPU usage is high or disk filled etc.&lt;/li&gt;
&lt;li&gt;High consumer lag can cause the degradation of other dependent services.&lt;/li&gt;
&lt;li&gt;JobManager of some Flink cluster becomes unhealthy.&lt;/li&gt;
&lt;li&gt;Sudden traffic increase on a service which demands resource resizing&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Siren can be used to develop alerting pipelines quickly in a self-serve manner. The following will get into the details of how Siren achieves this, by first laying some foundation of how alerting works, in general.&lt;/p&gt;
&lt;p&gt;With alerting, the aim is to notify via appropriate channels(Slack or Email or Pagerduty, etc.) when some event occurs(mostly bad events) so that developers are aware of the degradation and can take actions accordingly, well within time.&lt;/p&gt;
&lt;p&gt;For that, you’re required to first collect the health data of the system under observation. This is typically done by some monitoring server(e.g. Prometheus, Influx, etc.). You also set alerts on top of the collected metrics. Siren can be used to configure alerts inside the monitoring servers.
Monitoring Servers are referred to as &lt;a href=&quot;https://raystack.github.io/siren/docs/guides/provider_and_namespace&quot;&gt;Providers&lt;/a&gt; inside Siren. There are two ways of collecting metrics: push-based and pull-based.&lt;/p&gt;
&lt;p&gt;Monitoring servers provide ways to define alert conditions on top of this data. Think of it as a continuous evaluation of a script over a window of data.
One example script: Check if the CPU usage percentage is higher than 85% over a window of the past 10 minutes.&lt;/p&gt;
&lt;p&gt;Once the data is stored by the monitoring server, and the alert scripts(also called rules) are set, they are evaluated periodically on this data by the monitoring server’s rule engine. If the specified conditions match, an alert notification is sent in the configured notification mediums(ex. Email, Slack etc). Below is a dummy architecture diagram of this flow.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/58318bc11fb4685a0164223182145cc8/bd9eb/alerts.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 22.2972972972973%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA7ElEQVR42mN4+PBh6NWrV0OvXLkScvfu3eibN29qMjAwiAEx/8mTJyWAcuGHDx+O2rlzZ/yNGzfCz58/Lw2UY3j9+nXG////d797927Nmzdv1gPZ60E0A1DgzqlTp+4cOnToxtOnT9+8ffs2A6ieCYjZgIbZPX/+/M6DBw9ugjBQ7tb9+/cdQQYCHTILaMj/Z8+e/QbK/f/27dv/Fy9e/AfJMQLF4ZgBFbBKSkqKSElJySopKYkBaWEVFRV2kMTly5c1b9265fvo0SN3oKEeQIs8zp4968GAA8AM5gZikBdVgVgDiGWAmIcBDwAAuLCKiOeqM9oAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Overview of an alerting pipeline&quot;
        title=&quot;&quot;
        src=&quot;/static/58318bc11fb4685a0164223182145cc8/fcda8/alerts.png&quot;
        srcset=&quot;/static/58318bc11fb4685a0164223182145cc8/12f09/alerts.png 148w,
/static/58318bc11fb4685a0164223182145cc8/e4a3f/alerts.png 295w,
/static/58318bc11fb4685a0164223182145cc8/fcda8/alerts.png 590w,
/static/58318bc11fb4685a0164223182145cc8/efc66/alerts.png 885w,
/static/58318bc11fb4685a0164223182145cc8/c83ae/alerts.png 1180w,
/static/58318bc11fb4685a0164223182145cc8/bd9eb/alerts.png 1442w&quot;
        sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Siren provides the abstraction for defining alert conditions via &lt;a href=&quot;https://raystack.github.io/siren/docs/guides/rule&quot;&gt;Rules&lt;/a&gt; and routing configuration via &lt;a href=&quot;https://raystack.github.io/siren/docs/guides/subscription&quot;&gt;Subscriptions&lt;/a&gt; for a variety of monitoring providers. &lt;em&gt;It lets users define alert conditions and also the routing configurations to dispatch the notifications when alerts trigger.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;Siren Alert Configuration&lt;/h2&gt;
&lt;p&gt;Siren is written in Golang and &lt;a href=&quot;https://github.com/raystack/siren&quot;&gt;open-sourced&lt;/a&gt; under Raystack. Currently, it supports alert management for &lt;a href=&quot;https://cortexmetrics.io/&quot;&gt;Cortex metrics&lt;/a&gt;. We plan to add more monitoring providers e.g. Prometheus, Grafana Mimir, Thanos etc. (Your contributions are most welcome 🙏)&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/bb309936cb4b6e7711115c33f97ff575/fcda8/siren.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 56.75675675675676%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAAAsTAAALEwEAmpwYAAABnklEQVR42mNgIAPs37+fBURfefot8umnn8/fvX599e3bt/d+/vx5jAzj/jOG1v9nA9Fn/v9nffPmDd/169d5X79+zfvy5UsevBpRMQQYz/zPWl//n4mBKgBokHHaf9b///8zg7h3n70Pfv/x4/U3r18f+/bt27lfv37tYHj44b8gCL/595+P4T/CJSJFT9Skq15YiBc/N5eqeWIIMgzkVZAL04AYpObZm09aHz9/K/7w4UPWjx8/8r5//57McPj6385T1551Xrv7orB+8X8++/r/HAz2/1kM2t/tNOr5/l+v7cN/uZrXr2yr38qG9v7nJOjdo3c+ix2/9EL82rUnwmAXQl2p2fRWW7n6uYNq/TM7hYrn5qGr/jOHhl5hC62/wjbzDMSFjy5c0/ny4HbJuw8fsoGRk//p06cUMsLwDOsqaBjeP3814MvNyxfffPhwABjDJ4Be3wx00H9GGEbXjIJhsQw0MDR0FTMD9QAoHV4BpkMGkCNY/797x//v3z8+EAalRfKMhHr59pO34U9fvnr6+vWry+/evbsDzClHADhJBA8sLURUAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Siren&quot;
        title=&quot;&quot;
        src=&quot;/static/bb309936cb4b6e7711115c33f97ff575/fcda8/siren.png&quot;
        srcset=&quot;/static/bb309936cb4b6e7711115c33f97ff575/12f09/siren.png 148w,
/static/bb309936cb4b6e7711115c33f97ff575/e4a3f/siren.png 295w,
/static/bb309936cb4b6e7711115c33f97ff575/fcda8/siren.png 590w&quot;
        sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;In order to create alerts in a monitoring provider(e.g. Cortexmetrics), you should define a &lt;a href=&quot;https://raystack.github.io/siren/docs/guides/template&quot;&gt;Template&lt;/a&gt; first. The template has the alert body in the syntax as supported by the corresponding monitoring provider. In Prometheus compatible monitoring providers(Cortexmetrics, Thanos, Mimir etc) alert body is a yaml with 5 required keys: &lt;code class=&quot;language-text&quot;&gt;alert&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;expr&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;for&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;labels&lt;/code&gt;, and &lt;code class=&quot;language-text&quot;&gt;annotations&lt;/code&gt;. The template given below holds an array of templatized alerts of critical and warning severity.&lt;/p&gt;
&lt;p&gt;Think of templates as a blueprint for your actual alerts. Here is an example template that can be used to create alerts on CPU usage.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;apiVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; v2
&lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; template
&lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; CPU
&lt;span class=&quot;token key atrule&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;alert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; CPUWarning
    &lt;span class=&quot;token key atrule&quot;&gt;expr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; avg by (host) (cpu_usage_user&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;cpu=&quot;cpu&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;total&quot;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;) &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;.warning&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;[[.for]]&apos;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;severity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; WARNING
    &lt;span class=&quot;token key atrule&quot;&gt;annotations&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; CPU has been above &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;.warning&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; for last &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;.for&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; $labels.host &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;alert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; CPUCritical
    &lt;span class=&quot;token key atrule&quot;&gt;expr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; avg by (host) (cpu_usage_user&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;cpu=&quot;cpu&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;total&quot;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;) &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;.critical&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;[[.for]]&apos;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;severity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; CRITICAL
    &lt;span class=&quot;token key atrule&quot;&gt;annotations&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; CPU has been above &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;.critical&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; for last &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;.for&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; $labels.host &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;variables&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; for
    &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; string
    &lt;span class=&quot;token key atrule&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 10m
    &lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; For eg 5m&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 2h; Golang duration format
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; warning
    &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; int
    &lt;span class=&quot;token key atrule&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;80&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; critical
    &lt;span class=&quot;token key atrule&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; int
    &lt;span class=&quot;token key atrule&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;90&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;tags&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; systems&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This template is evaluated by &lt;a href=&quot;https://pkg.go.dev/text/template&quot;&gt;go-templates&lt;/a&gt; with &lt;code class=&quot;language-text&quot;&gt;[[ ]]&lt;/code&gt; being the delimiter. As you can see, in the list of “variables”, &lt;code class=&quot;language-text&quot;&gt;for&lt;/code&gt; , &lt;code class=&quot;language-text&quot;&gt;warning&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;critical&lt;/code&gt; have been templatized. It denotes the default values and the data type as well. The user needs to give value to these “variables” during alert creation. After a valid request payload, an alert will be created by Siren. This alert can be further enabled/disabled using Siren APIs.&lt;/p&gt;
&lt;p&gt;Templates provide a means of achieving reusability. A generic template (like the one shown above) can be used by multiple teams in multiple environments (i.e. dev, prod etc.) without any alert “configuration drift”. So based on the needs, teams can define their templates and create alerts from them using Siren APIs, instead of rewriting the same alert rule every other time.&lt;/p&gt;
&lt;p&gt;Templates allow us to simplify alerting pipeline it to an extent that users don’t even need to know all the details of the alerting rule because most of the complexity is abstracted out by the Siren APIs. It is done by creating an alert marketplace of sorts, where people can read the description of the alert and provide just enough input to create the alert on their data pipeline. Here is a snap of this beauty:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6d02ad2db7c38e4dcc00392ace87d245/b17f8/alert-window.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 53.37837837837838%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAQDBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAa+hBabA/8QAGRAAAQUAAAAAAAAAAAAAAAAAAAIDEDEy/9oACAEBAAEFAhGYbo//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAWEAADAAAAAAAAAAAAAAAAAAAAASD/2gAIAQEABj8CFP8A/8QAGRABAAIDAAAAAAAAAAAAAAAAAQAQEVGh/9oACAEBAAE/ISc1Ea46r//aAAwDAQACAAMAAAAQi8//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAVEQEBAAAAAAAAAAAAAAAAAAAQEf/aAAgBAgEBPxCH/8QAGxAAAQQDAAAAAAAAAAAAAAAAAQAQETEhUZH/2gAIAQEAAT8QozQNKinA4AAcb//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Alerting made easy with Siren&quot;
        title=&quot;&quot;
        src=&quot;/static/6d02ad2db7c38e4dcc00392ace87d245/1c72d/alert-window.jpg&quot;
        srcset=&quot;/static/6d02ad2db7c38e4dcc00392ace87d245/a80bd/alert-window.jpg 148w,
/static/6d02ad2db7c38e4dcc00392ace87d245/1c91a/alert-window.jpg 295w,
/static/6d02ad2db7c38e4dcc00392ace87d245/1c72d/alert-window.jpg 590w,
/static/6d02ad2db7c38e4dcc00392ace87d245/a8a14/alert-window.jpg 885w,
/static/6d02ad2db7c38e4dcc00392ace87d245/fbd2c/alert-window.jpg 1180w,
/static/6d02ad2db7c38e4dcc00392ace87d245/b17f8/alert-window.jpg 1600w&quot;
        sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;After the Save button click, this user interface calls Siren Rules PUT API with the input provided by User. Siren identifies the appropriate provider and configures these alerts inside it. The same alerts can be edited again and again, like disabling and enabling with the thresholds preserved and changing thresholds.&lt;/p&gt;
&lt;p&gt;With the primary use-case of alert management being solved, we looked for an elegant developer experience where we provide a CLI interface and GitOps model of alert management.&lt;/p&gt;
&lt;p&gt;These interfaces are quite popular with teams that need alert management in bulk. The rules are uploaded in a git repo and a CI pipeline ensures corresponding alerts are set in the monitoring server in the exact same way. Read more about it &lt;a href=&quot;https://raystack.github.io/siren/docs/guides/rule#use-case-ci&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Siren Alert Routing Configuration&lt;/h2&gt;
&lt;p&gt;Siren also provides a way to define the routing of the triggered alerts in a similar fashion.&lt;/p&gt;
&lt;p&gt;As we know, the monitoring server continuously evaluates the alerts and sends a notification when the alert fires. So there needs to be appropriate configurations set beforehand (with the required authorization) in order to send out those notifications.&lt;/p&gt;
&lt;p&gt;For Prometheus compatible monitoring servers(like Cortexmetrics, Mimir etc.), these configs are set into &lt;a href=&quot;https://prometheus.io/docs/alerting/latest/alertmanager/&quot;&gt;Alertmanager&lt;/a&gt;. All of it can be done via Siren in a self-serve way using Siren. Here is an example of how we let users define alert routing in a UI.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/38750d5368eb7860778660547d25c597/b17f8/alert-integration.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 53.37837837837838%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwABAQEBAAAAAAAAAAAAAAAAAAQDBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAa+hBabA/8QAGRAAAQUAAAAAAAAAAAAAAAAAAAIDEDEy/9oACAEBAAEFAhGYbo//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAWEAADAAAAAAAAAAAAAAAAAAAAASD/2gAIAQEABj8CFP8A/8QAGRABAAIDAAAAAAAAAAAAAAAAAQAQEVGR/9oACAEBAAE/ISHlRGuOq//aAAwDAQACAAMAAAAQi8//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/ED//xAAVEQEBAAAAAAAAAAAAAAAAAAAQEf/aAAgBAgEBPxCH/8QAGxAAAgIDAQAAAAAAAAAAAAAAAAERIRAxUZH/2gAIAQEAAT8QF2IXDQnB0ijzH//Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Setting up the routing of alerts powered by Siren&quot;
        title=&quot;&quot;
        src=&quot;/static/38750d5368eb7860778660547d25c597/1c72d/alert-integration.jpg&quot;
        srcset=&quot;/static/38750d5368eb7860778660547d25c597/a80bd/alert-integration.jpg 148w,
/static/38750d5368eb7860778660547d25c597/1c91a/alert-integration.jpg 295w,
/static/38750d5368eb7860778660547d25c597/1c72d/alert-integration.jpg 590w,
/static/38750d5368eb7860778660547d25c597/a8a14/alert-integration.jpg 885w,
/static/38750d5368eb7860778660547d25c597/fbd2c/alert-integration.jpg 1180w,
/static/38750d5368eb7860778660547d25c597/b17f8/alert-integration.jpg 1600w&quot;
        sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Siren handles the internals to map these configs to the appropriate team, so that any alert owned by a team, reaches only their channels when alerts fire. This is achieved by Siren’s &lt;a href=&quot;https://raystack.github.io/siren/docs/guides/subscription&quot;&gt;Subscriptions&lt;/a&gt;. You “subscribe” to alerts by defining “matchers” on &lt;a href=&quot;https://raystack.github.io/siren/docs/guides/receiver&quot;&gt;receivers&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The “matchers” are a set of conditions(key-value pairs, kind of a label selectors of k8s) that must match on a given triggered alert for the subscription to be selected for alert routing. Receivers are an abstraction for actual notification medium with authorization support baked in. For example: a “slack” receiver holds the auth-token and workspace name such that alerts can be routed in the slack channels of that workspace on demand.&lt;/p&gt;
&lt;p&gt;For example, the image above configures alert routing for a &lt;a href=&quot;https://github.com/raystack/firehose&quot;&gt;Firehose&lt;/a&gt; deployment called example-firehose. The matchers roughly translate to:&lt;/p&gt;
&lt;p&gt;Whenever some alert has &lt;code class=&quot;language-text&quot;&gt;severity: critical&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;team: exampleTeam&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;deployment: example-firehose&lt;/code&gt; the alert will be routed to two receivers:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Slack with channel name as given in user input&lt;/li&gt;
&lt;li&gt;Pagerduty with service key given in input.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Using subscriptions, you can define any matching condition and pick receivers to handle the routing. Siren subscriptions also abstract out the complexity of alert routing configuration for many providers(e.g. Cortexmetrics, Influx, etc.) via a single API interface.&lt;/p&gt;
&lt;p&gt;To my knowledge, in production, it manages the configuration of approximately 1500 alerts and the routing of around 90 teams. By utilizing Siren, we ensure the overall health of services &amp;#x26; products within the data platform.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;That was a high-level overview of the functionality provided by Siren. We encourage you to share your comments and ask any questions you may have 🙌. Additionally, you can access the Siren &lt;a href=&quot;https://raystack.github.io/siren/&quot;&gt;documentation&lt;/a&gt;, which offers detailed explanations of concepts, terminologies, database schema, API contracts, usage guidelines, and various other informative guides.&lt;/p&gt;
&lt;p&gt;Siren is currently in its early stages, and we are eagerly anticipating its potential impact on the open-source community. Our goals include expanding support for multiple monitoring providers and tackling challenging use cases, like enabling alert subscriptions without dependency on internal IAM policies.&lt;/p&gt;
&lt;p&gt;If you’re interested in contributing, we invite you to explore the list of &lt;a href=&quot;https://github.com/raystack/siren/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22&quot;&gt;good first issues&lt;/a&gt;.
available. Your contributions would be highly valued and appreciated. Thanks for reading!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Google IAP auth automation for CLI apps]]></title><description><![CDATA[Automating Google IAP authentication for CLI tools — a real automation project from Gojek, finally written up.]]></description><link>https://absh.dev/oauth/</link><guid isPermaLink="false">https://absh.dev/oauth/</guid><pubDate>Sat, 15 Jul 2023 22:14:03 GMT</pubDate><content:encoded>&lt;p&gt;This blog takes inspiration from a significant automation project I undertook at Gojek in 2021. It has been pending publication for a while, mainly due to my lack of diligence. However, I have now resolved to be more active in blogging, starting with the unpublished drafts I have accumulated. Without further ado, let’s dive into it.&lt;/p&gt;
&lt;h2&gt;Automating Google IAP Auth for Golang applications&lt;/h2&gt;
&lt;p&gt;At Gojek, various internal applications for Developers uses Google SSO. The Data Platform team also uses Google Authorization Server as the identity provider to identify the developer and enforce proper ACL. We use &lt;a href=&quot;https://cloud.google.com/iap&quot;&gt;Google IAP&lt;/a&gt; to guard access to our applications.&lt;/p&gt;
&lt;p&gt;Google IAP is widely used to control access to web based application running on browsers. The same applications had a CLI based client, where one can perform similar action using command line. Think of it like Google cloud console and &lt;code class=&quot;language-text&quot;&gt;gcloud&lt;/code&gt; CLI utility.&lt;/p&gt;
&lt;p&gt;We wanted to enable our developers to perform the same actions using the CLI applications. The challenge was to authenticate our users and service accounts.&lt;/p&gt;
&lt;p&gt;Google IAP performs authentication of the users using Google Single Sign-On and authorizes them against Cloud IAM policies before accessing the protected resource. Before diving deep into the solution, let’s see how it works behind the scene. Here we need to understand two terminologies: OAuth and OIDC.&lt;/p&gt;
&lt;h3&gt;OAuth 2.0&lt;/h3&gt;
&lt;p&gt;OAuth 2.0 is an industry-standard for allowing applications to perform some action on behalf of a user into some other application. For example, you can securely allow a 3rd party application to access your Google contact list without giving them your Google account username and password.&lt;/p&gt;
&lt;p&gt;Here is how a typical OAuth 2.0 Flow works. You want to allow a 3rd party application (let’s call it 3PA, from hereon) to access your Google contacts list. The sequence of steps that happens under OAuth 2.0 flow is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;3PA prompts you to fetch your contacts list in the browser(ex: using some button prompt.)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;3PA redirects your browser to the authorization server, Google in our case. In this request, 3PA will include these three things: client ID, redirect URL, scope(read-contacts).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The Google authorization server verifies your session or prompts you to log in (if not logged in already). Then, it shows you the consent form, asking you if you want the 3PA to access the data per the scope(read-contacts). You could choose to allow or deny.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If allowed by you, the Google authorization server redirects back to the 3PA(on redirect URL) with a temporary authorization code.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;3PA then directly calls Google authorization server with the client ID, client secret and authorization code to get an &lt;code class=&quot;language-text&quot;&gt;access_token&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The 3PA can use this access token as a key to access users data. Such requests get authorized by the server which owns the user’s resources(contacts list).&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;One more thing to note here is that the authorization server issues client ID and the secret to the 3PA via a one-time registration flow. Read &lt;a href=&quot;https://developers.google.com/workspace/guides/create-credentials&quot;&gt;this&lt;/a&gt; to learn how to create access credentials.&lt;/p&gt;
&lt;h3&gt;OIDC&lt;/h3&gt;
&lt;p&gt;What’s missing in OAuth 2.0 protocol is that it doesn’t tell 3PA about who you are and anything about you; instead, it only grants 3PA to access your data on your behalf. Enters OIDC.&lt;/p&gt;
&lt;p&gt;OIDC is a thin layer on top of OAuth 2.0, which adds functionality around login and profile information along with what OAuth 2.0 offers. So any client using OIDC protocol can establish a login session on behalf of the user. The authorization server supporting OIDC is providing the identity, also referred to as identity-provider. Remember the SSO login screens you see on various websites?&lt;/p&gt;
&lt;p&gt;OIDC flow is very similar to OAuth 2.0 with a slight modification.&lt;/p&gt;
&lt;p&gt;Here is how a typical OIDC flow differs:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;In Step 2, 3PA redirects the user to the Google Authorization server, with the client ID, secret and a specific scope “OpenID”. So the authorization server knows that this will be an OIDC exchange.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In Step 5: The authorization server sent an &lt;code class=&quot;language-text&quot;&gt;access_token&lt;/code&gt; in exchange for the authorization code. With OIDC scope, the authorization server will also send one more token named &lt;code class=&quot;language-text&quot;&gt;id_token&lt;/code&gt;. Here, 3PA gets two tokens, &lt;code class=&quot;language-text&quot;&gt;access_token&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;id_token&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;id_token&lt;/code&gt; is a JWT meaning 3PA can extract information(called claims) from it, such as name, email id, username, expiration etc.&lt;/p&gt;
&lt;p&gt;Considering the previously mentioned sequence, we can explore the implementation of the aforementioned process in a Golang Applications.&lt;/p&gt;
&lt;h3&gt;Solution&lt;/h3&gt;
&lt;p&gt;We found a reasonably decent open-sourced Golang OIDC client library &lt;a href=&quot;https://github.com/bwplotka/oidc&quot;&gt;bwplotka/oidc&lt;/a&gt; to start from. With slight modifications as per the Google IAP &lt;a href=&quot;https://cloud.google.com/iap/docs/authentication-howto&quot;&gt;contract&lt;/a&gt;, we made this library compatible to be used by our applications.&lt;/p&gt;
&lt;p&gt;After using the modified library embedded in our application in production, we felt this solution could help others as well. So we extracted it out as a standalone library called Casper.&lt;/p&gt;
&lt;p&gt;This is what Casper does:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Casper gives you an authenticated HTTP Golang Client for applications to make requests on behalf of users and service accounts.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;It also cares about refreshing the token when expired and cache the token in the system keyring for future needs.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Example usage for a User authentication&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;go&quot;&gt;&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    casper &lt;span class=&quot;token string&quot;&gt;&quot;github.com/odpf/casper&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

audience &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1234-xxx.apps.googleusercontent.com&quot;&lt;/span&gt;

authenticatedHTTPclient&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; casper&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;GetAuthenticatedClient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;audience&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	fmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Error while getting authenticated HTTP client: &quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The client ID and secret can also be provided as Golang variables. If not provided, Casper falls back to ENV variables &lt;code class=&quot;language-text&quot;&gt;GOOGLE_OAUTH2_CLIENT_ID&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;GOOGLE_OAUTH2_CLIENT_SECRET&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example usage for a Service Account&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;go&quot;&gt;&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    casper &lt;span class=&quot;token string&quot;&gt;&quot;source.golabs.io/asgard/casper&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

filename &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/path/to/service/account/key.json&quot;&lt;/span&gt;
audience &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1234-xxx.apps.googleusercontent.com&quot;&lt;/span&gt;

authenticatedHTTPclient&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; casper&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;GetAuthenticatedClient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
	audience&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; casper&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WithServiceAccountCredentials&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;filename&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; err &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	fmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Error while getting authenticated HTTP client: &quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The service account key is used to authenticate a Service account.
Have a look at the Documentation to learn more about the usage ✌️.&lt;/p&gt;
&lt;h3&gt;Aftermath&lt;/h3&gt;
&lt;p&gt;Casper has removed the overhead for developers to automate Google IAP Auth flow in their Golang Applications, saving their time and effort so that they can focus on the actual problem at hand.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Docker Containers Read Only Mount]]></title><description><![CDATA[Running containers with read-only filesystems and managing state safely with external mounts.]]></description><link>https://absh.dev/docker-read-only-mount/</link><guid isPermaLink="false">https://absh.dev/docker-read-only-mount/</guid><pubDate>Sat, 10 Sep 2022 22:15:03 GMT</pubDate><content:encoded>&lt;p&gt;Docker containers are best suited for stateless application. But they also provide ways to manage state externally in an efficient and reliable manner. The developers need to be cautious while using external storage with containers. Here we discuss how to use external storage and some example best practices around it.&lt;/p&gt;
&lt;h3&gt;Docker Volumes&lt;/h3&gt;
&lt;p&gt;For application that rely on an external state, containers are not the best fit solution for deployment. If you store the state inside container, it will be lost once the container is stopped. Volumes are used for this specific reason. You can choose to store the state outside of your container, so that data remains intact even if container is stopped and removed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Demo&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Let’s use host machine’s permanent storage for storing some contents from inside a container. You can mount any directory from host machine inside the container machine.&lt;/p&gt;
&lt;p&gt;Let’s create an directory &lt;code class=&quot;language-text&quot;&gt;demo&lt;/code&gt; on host machine and create a text file inside it.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ cd ~
$ mkdir demo
$ cd demo
$ echo &quot;foo bar&quot; &gt; hello.txt&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now let’s mount this directory in a docker container using the &lt;code class=&quot;language-text&quot;&gt;-v&lt;/code&gt; CLI option.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ docker run --rm -ti -v ~/demo:/data: alpine:latest /bin/sh
/ # cat /data/hello.txt
foo bar
/ #&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As you can see, we are able to access the contents of &lt;code class=&quot;language-text&quot;&gt;~/demo&lt;/code&gt; which is in the host machine, mounted at &lt;code class=&quot;language-text&quot;&gt;/data&lt;/code&gt; inside our container.&lt;/p&gt;
&lt;p&gt;The usage patterns are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Any changes made inside &lt;code class=&quot;language-text&quot;&gt;~/demo&lt;/code&gt; directory will be visible inside container at &lt;code class=&quot;language-text&quot;&gt;/data&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Any changes made in &lt;code class=&quot;language-text&quot;&gt;/data&lt;/code&gt; from inside the container will be visible in &lt;code class=&quot;language-text&quot;&gt;~/demo&lt;/code&gt; of the host machine.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Leveraging read-only mounts&lt;/h3&gt;
&lt;p&gt;It is also possible to mount directories as read only mode. You can do that by passing &lt;code class=&quot;language-text&quot;&gt;ro&lt;/code&gt; flag e.g.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; run &lt;span class=&quot;token parameter variable&quot;&gt;--rm&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-ti&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-v&lt;/span&gt; ~/demo:/data:ro alpine:latest /bin/sh&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Extending on this idea, it is a good practice to mount your root volume of your container as read only mode, so that process within the container cannot write anything to the root filesystem of the container.&lt;/p&gt;
&lt;p&gt;This way you can enforce all writes to go to the expected external storages mounted on different paths, so that the writes are not lost when container is stopped/removed.&lt;/p&gt;
&lt;h4&gt;Mounting root volume as read-only&lt;/h4&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;--read-only&lt;/code&gt; flag let’s you enforce just that.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;docker run --rm -ti --read-only=true -v ~/demo:/data alpine:latest /bin/sh&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In the above scenarios, when you run &lt;code class=&quot;language-text&quot;&gt;mount&lt;/code&gt; you get a output like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;/ # mount
overlay on / type overlay (ro,relatime,lowerdir=/var/lib/doc...)
grpcfuse on /data type fuse.grpcfuse (rw,nosuid,nodev,relatime,user_id=0,group_id=0,allow_other,max_read=1048576)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;/&lt;/code&gt; is ro(read only) while directory mounted at &lt;code class=&quot;language-text&quot;&gt;/data&lt;/code&gt; is rw(read write).&lt;/p&gt;
&lt;p&gt;In a nutshell&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Mount root filesystem of container as read only permissions&lt;/li&gt;
&lt;li&gt;Mount external volumes with read-write permissions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are multiple benefits in doing this like this saves you from accidentally writing log-files(which the developer might be unaware of) in container’s internal filesystem and filling up the disk space allocated to container in production.&lt;/p&gt;
&lt;p&gt;One should also be cautious while enforcing this as this is quite restrictive. It will not allow any files to be created in &lt;code class=&quot;language-text&quot;&gt;/&lt;/code&gt; by any process. Your applications must adjust to this restriction accordingly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;tmpfs&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;But there is a workaround to this as well. You can have &lt;code class=&quot;language-text&quot;&gt;/tmp&lt;/code&gt; as writeable directory and rest of the container as read only. Use &lt;code class=&quot;language-text&quot;&gt;--tmpfs&lt;/code&gt; argument to mount a &lt;code class=&quot;language-text&quot;&gt;tmpfs&lt;/code&gt; file system into the container. Although it is ephemeral i.e. the changes will be lost when container is stopped.&lt;/p&gt;
&lt;p&gt;Here in the example below we are launching a 256MB RW temporary storage.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;docker run --rm -ti --read-only=true -v ~/demo:/data --tmpfs /tmp:rw,noexec,nodev,nosuid,size=256M alpine:latest /bin/sh&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;em&gt;PS: This article was inspired from the &lt;a href=&quot;https://www.oreilly.com/library/view/docker-up/9781492036722/&quot;&gt;Docker Up and Running book&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Reducing Docker Images size]]></title><description><![CDATA[Why image size matters at fleet scale, and concrete techniques to shrink Docker images for faster, cheaper deploys.]]></description><link>https://absh.dev/docker-history/</link><guid isPermaLink="false">https://absh.dev/docker-history/</guid><pubDate>Sat, 10 Sep 2022 22:13:03 GMT</pubDate><content:encoded>&lt;p&gt;Docker has become a defacto standard of building, distributing and running stateless softwares these days. Companies deploy large fleet of containers everyday via their CI/CD pipelines.&lt;/p&gt;
&lt;p&gt;It would be interesting to look into the efficiency of docker builds mainly in terms of optimizing the size of container to make it even faster to release and download images.&lt;/p&gt;
&lt;h3&gt;docker history&lt;/h3&gt;
&lt;p&gt;We will talk about &lt;code class=&quot;language-text&quot;&gt;docker history&lt;/code&gt; command here.&lt;/p&gt;
&lt;p&gt;Let’s look at a demo.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;dockerfile&quot;&gt;&lt;pre class=&quot;language-dockerfile&quot;&gt;&lt;code class=&quot;language-dockerfile&quot;&gt;&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; fedora&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;RUN&lt;/span&gt; dnf install -y httpd&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;CMD&lt;/span&gt; [&lt;span class=&quot;token string&quot;&gt;&quot;/usr/sbin/httpd&quot;&lt;/span&gt;, &lt;span class=&quot;token string&quot;&gt;&quot;-DFOREGROUND&quot;&lt;/span&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;After building an image from this Dockerfile, the image size is ~466MB. In order to optimize the size, you should first look into each layers contribution in the final size.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;docker history test&lt;/code&gt; will yield just that:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;docker history test

IMAGE          CREATED         CREATED BY                                      SIZE      COMMENT
b5b5ab1ead58   5 seconds ago   CMD [&quot;/usr/sbin/httpd&quot; &quot;-DFOREGROUND&quot;]          0B        buildkit.dockerfile.v0
&amp;lt;missing&gt;      5 seconds ago   RUN /bin/sh -c dnf install -y httpd # buildk…   288MB     buildkit.dockerfile.v0
&amp;lt;missing&gt;      4 months ago    /bin/sh -c #(nop)  CMD [&quot;/bin/bash&quot;]            0B
&amp;lt;missing&gt;      4 months ago    /bin/sh -c #(nop) ADD file:0ee69bd7b31ad9a58…   178MB
&amp;lt;missing&gt;      5 months ago    /bin/sh -c #(nop)  ENV DISTTAG=f36container …   0B
&amp;lt;missing&gt;      10 months ago   /bin/sh -c #(nop)  LABEL maintainer=Clement …   0B
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The size of the image is 466MB. As you can see, surprisingly, &lt;code class=&quot;language-text&quot;&gt;fedora&lt;/code&gt; image was pulled and it was about 178MB, and the &lt;code class=&quot;language-text&quot;&gt;httpd&lt;/code&gt; took almost 290MB. It’s way higher than the whole fedora, definitely something is not right.&lt;/p&gt;
&lt;p&gt;As it turns out, &lt;code class=&quot;language-text&quot;&gt;dnf&lt;/code&gt; and other popular package manager rely on a cache that store info about all the packages that are available for installation on that platform. We can easily reduce the size by getting rid of this huge cache.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;dockerfile&quot;&gt;&lt;pre class=&quot;language-dockerfile&quot;&gt;&lt;code class=&quot;language-dockerfile&quot;&gt;&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; fedora&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;RUN&lt;/span&gt; dnf install -y httpd &amp;amp;&amp;amp; &lt;span class=&quot;token operator&quot;&gt;\&lt;/span&gt;
    dnf clean all&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;CMD&lt;/span&gt; [&lt;span class=&quot;token string&quot;&gt;&quot;/usr/sbin/httpd&quot;&lt;/span&gt;, &lt;span class=&quot;token string&quot;&gt;&quot;-DFOREGROUND&quot;&lt;/span&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;On building the image again, after this cached repository metadata, we get this :&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;docker history test

IMAGE          CREATED         CREATED BY                                      SIZE      COMMENT
8596a91ffcea   6 seconds ago   CMD [&quot;/usr/sbin/httpd&quot; &quot;-DFOREGROUND&quot;]          0B        buildkit.dockerfile.v0
&amp;lt;missing&gt;      6 seconds ago   RUN /bin/sh -c dnf install -y httpd &amp;amp;&amp;amp;     d…   52.3MB    buildkit.dockerfile.v0
&amp;lt;missing&gt;      4 months ago    /bin/sh -c #(nop)  CMD [&quot;/bin/bash&quot;]            0B
&amp;lt;missing&gt;      4 months ago    /bin/sh -c #(nop) ADD file:0ee69bd7b31ad9a58…   178MB
&amp;lt;missing&gt;      5 months ago    /bin/sh -c #(nop)  ENV DISTTAG=f36container …   0B
&amp;lt;missing&gt;      10 months ago   /bin/sh -c #(nop)  LABEL maintainer=Clement …   0B&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The new size of the image is 231 MB. We were able to get drastic size optimization.&lt;/p&gt;
&lt;p&gt;In conclusion, &lt;code class=&quot;language-text&quot;&gt;docker history&lt;/code&gt; is a powerful tool that can be used to inspect the layers and how much they add up to the final size of the image. Reducing docker images size has direct implication on application distribution and deployment time.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;PS: This article was inspired from the &lt;a href=&quot;https://www.oreilly.com/library/view/docker-up/9781492036722/&quot;&gt;Docker Up and Running book&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Troubleshooting Docker build failure]]></title><description><![CDATA[Practical techniques for debugging Docker build failures, including using intermediate containers to inspect failing steps.]]></description><link>https://absh.dev/book-notes-docker-up-and-running/</link><guid isPermaLink="false">https://absh.dev/book-notes-docker-up-and-running/</guid><pubDate>Sat, 10 Sep 2022 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;When you encounter some error in docker build, there are efficient ways of debugging it like - “Using an intermediate container to debug next commands”.&lt;/p&gt;
&lt;p&gt;For example: here is a failing build log:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;shell&quot;&gt;&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;Step &lt;span class=&quot;token number&quot;&gt;6&lt;/span&gt;/14 &lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; ENV SCPATH /etc/supervisor/conf.d
 ---&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; Running &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; 9c0a385269cf
Removing intermediate container 9c0a385269cf
 ---&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; 8a773166616c
Step &lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt;/14 &lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; RUN &lt;span class=&quot;token function&quot;&gt;apt-get&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-y&lt;/span&gt; update-all
 ---&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; Running &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; cd57fc47503d
E: Command line option &lt;span class=&quot;token string&quot;&gt;&apos;y&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;from -y&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; is not known.
The &lt;span class=&quot;token builtin class-name&quot;&gt;command&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;/bin/sh -c apt-get -y update-all&apos;&lt;/span&gt; returned a non-zero code: &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As you can see, the command on step 7 &lt;code class=&quot;language-text&quot;&gt;apt-get -y update-all&lt;/code&gt; has failed. We know that the command is malformed (since it’s just a demo), but instead editing the command and rerunning docker build, we can check the correctness of the command in an isolated environment itself.&lt;/p&gt;
&lt;p&gt;Step 7 uses the container with id &lt;code class=&quot;language-text&quot;&gt;8a773166616c&lt;/code&gt; to run the command.&lt;/p&gt;
&lt;p&gt;We can use the same container to interactively debug what’s the issue:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ docker run --rm -ti 8a773166616c /bin/bash
root@464e8e35c784:/#
root@464e8e35c784:/# apt-get -y update-all
E: Command line option &apos;y&apos; [from -y] is not known.

root@464e8e35c784:/# apt-get update-all
E: Invalid operation update-all

root@464e8e35c784:/# apt-get -y update
Get:1 http://security.debian.org jessie/updates InRelease [63.1 kB]
...
Reading package lists... Done&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Once you have found the issue with the command, you can fix it in the Dockerfile.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Golang WaitGroup]]></title><description><![CDATA[A deep dive into Go's sync.WaitGroup: how it works under the hood and how to use it without shooting yourself in the foot.]]></description><link>https://absh.dev/golang-wait-group/</link><guid isPermaLink="false">https://absh.dev/golang-wait-group/</guid><pubDate>Sun, 06 Feb 2022 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;Golang provides a set of concurrency primitives which helps programmers write efficient concurrent code. &lt;code class=&quot;language-text&quot;&gt;WaitGroup&lt;/code&gt; is one of the constructs of Golang Concurrency. Let’s take a deep dive into how it works and how to best use it.&lt;/p&gt;
&lt;h2&gt;Why use WaitGroup&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;WaitGroups&lt;/code&gt; are an ideal way for you to &lt;strong&gt;wait&lt;/strong&gt; for some set of concurrent operations to complete. Example: Wait for child processes to complete before parent process exits.&lt;/p&gt;
&lt;p&gt;Let’s take a simple example.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;go&quot;&gt;&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;package&lt;/span&gt; main

&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;fmt&quot;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;greet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	fmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Hello&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	fmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Starting main...&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;go&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;greet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	fmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Exiting main...&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The output of the program comes to be:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;$ go run main.go

Starting main&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
Exiting main&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In the program above, the main function is not waiting for the goroutine &lt;code class=&quot;language-text&quot;&gt;greet&lt;/code&gt; to finish executing. So we do not see the result on stdout from the goroutine.&lt;/p&gt;
&lt;h3&gt;Usage&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;WaitGroup&lt;/strong&gt; can be used here to wait for the concurrent function to complete before exiting the main.&lt;/p&gt;
&lt;p&gt;In the modified version:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;go&quot;&gt;&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;package&lt;/span&gt; main

&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
	&lt;span class=&quot;token string&quot;&gt;&quot;fmt&quot;&lt;/span&gt;
	&lt;span class=&quot;token string&quot;&gt;&quot;sync&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; greetWaiter sync&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;WaitGroup

&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;greet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;defer&lt;/span&gt; greetWaiter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Done&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	fmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Hello&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	fmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Starting main...&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	greetWaiter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;go&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;greet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	greetWaiter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Wait&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	fmt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Println&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Exiting main...&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The output you get:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;$ go run main.go

Starting main&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.
Hello
Exiting main&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In the modified snippet, the noteworthy changes are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We are creating a WaitGroup object &lt;code class=&quot;language-text&quot;&gt;var greetWaiter sync.WaitGroup&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;When it is time to call the goroutine we increment the adder by 1: &lt;code class=&quot;language-text&quot;&gt;greetWaiter.Add(1)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Until we are done with the goroutine, we want to block the execution on main. So we wait till the goroutine finishes execution: &lt;code class=&quot;language-text&quot;&gt;greetWaiter.Wait()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;From inside the goroutine, we signal done when the goroutine finishes: &lt;code class=&quot;language-text&quot;&gt;defer greetWaiter.Done()&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Waitgroup Constructs&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Add(n)&lt;/code&gt;: Indicates to the WaitGroup, that &lt;code class=&quot;language-text&quot;&gt;n&lt;/code&gt; goroutines are beginning.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Done()&lt;/code&gt;: Indicates completion of the goroutine who calls it. This is typically used with &lt;code class=&quot;language-text&quot;&gt;defer&lt;/code&gt;, as a part of the various clean-up actions.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Wait()&lt;/code&gt;: Will block the function that has called &lt;code class=&quot;language-text&quot;&gt;wait()&lt;/code&gt;, until all the goroutines have indicated that they have exited.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Summary&lt;/h3&gt;
&lt;p&gt;In summary, we can think of &lt;code class=&quot;language-text&quot;&gt;WaitGroup&lt;/code&gt; as a thread-safe counter for tracking various goroutine’s execution and exit within a program.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Horizontally scaling up a Kafka cluster ]]></title><description><![CDATA[A production story of scaling Kafka horizontally under load — partition reassignment, the trade-offs vs vertical scaling, and lessons from the outage that prompted it.]]></description><link>https://absh.dev/kafka-scaling/</link><guid isPermaLink="false">https://absh.dev/kafka-scaling/</guid><pubDate>Sat, 11 Sep 2021 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;I work in the Data Engineering team in Gojek, and my work involves managing lots of Kafka clusters, among other kinds of stuff. This blog is inspired by a recent outage in one of our Kafka cluster, causing degradation in the consumer applications. As a resort, we had to scale up the Kafka cluster horizontally. This blog will acquaint you with horizontally scaling up a Kafka cluster, spotlighting partition reassignment.&lt;/p&gt;
&lt;p&gt;Due to a high load on the cluster, we were facing high CPU on few brokers. We had an option to either increase the computing power on each broker simply by replacing them with powerful machines, i.e. vertical scaling. Vertically scaling a Kafka cluster means replacing existing broker nodes with higher capacity nodes while keeping the same brokers.&lt;/p&gt;
&lt;p&gt;But there are a few challenges when you do that. For example, it involves careful re-consideration of the broker configs to use the new compute power optimally. Over-provisioning of computing and storage resources to the cluster can be quite costly to the business. We didn’t want to put much effort into recalibrating our broker configurations as per new machine specs to save some time in a critical production support issue. So we decide to go for horizontally scaling the Kafka cluster.&lt;/p&gt;
&lt;p&gt;Our Kafka brokers are usually standard Google Compute Engine instances. The broker configurations are defined in reusable IaC modules so that many teams can benefit from these well-thought configurations. All you need to do is provision your brokers using the IaC tool. In the Data Engineering team, we heavily use Terraform for IaC provisioning.&lt;/p&gt;
&lt;h2&gt;Process&lt;/h2&gt;
&lt;p&gt;Horizontally scaling the Kafka cluster involves these rough steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create new machines provisioning storage, networking, and compute resources&lt;/li&gt;
&lt;li&gt;Start the brokers with your chosen configurations and provisioned resources&lt;/li&gt;
&lt;li&gt;Reassign partitions across the cluster so that the new brokers share the load and the cluster’s overall performance improves&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Steps 1 and 2 can be challenging, time-consuming, error-prone and tricky when you don’t have a standard way of creating new machines and provisioning them with necessary software and configurations. The Data Engineering team has diligently put lots of effort into achieving these standards across any deployment landscape, be it containers or virtual machines. It gives us exponential benefits saving a lot of time, avoiding configuration drift and human errors. So here, the only thing left to figure out was &lt;em&gt;reassignment of partitions&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Before scaling up, the cluster had six brokers and ~1600 topics. As you might know, Kafka topics are further split into partitions to load balance. Brokers store these partitions. Brokers replicate partitions as per the replication configuration. If you are interested in a refresher, I’ve written about these concepts &lt;a href=&quot;https://abhisheksah.xyz/what-makes-kafka-awesome/&quot;&gt;here&lt;/a&gt;. When you add new brokers, you also need to manually rearrange the partitions across the cluster so that the new brokers can balance the load evenly across all brokers.&lt;/p&gt;
&lt;p&gt;For each partition of a topic, one broker gets elected as leader of that partition, and a few brokers participate in replicating that partition. So, for example: for a Topic T with replication factor 3, a partition Pi is distributed among Broker 1 as leader and Broker 2 and 3 as followers. When you run reassignment on this topic, leaders and followers will get modified.&lt;/p&gt;
&lt;p&gt;Let’s see a detailed example. The image below shows a topic &lt;code class=&quot;language-text&quot;&gt;test-topic&lt;/code&gt; with 12 partitions and a storage plan across nine brokers.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 319px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/c594a1422f5ed095d1fe246ce0407d75/b53cf/before_reassignment.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 114.1891891891892%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAXCAYAAAALHW+jAAAACXBIWXMAAAsTAAALEwEAmpwYAAACNElEQVR42n2V167jMAxEnd5777135CXJ//8XF4cXNCzF2QfCReZoOBzKwX6/F2I8HksqlZJkMinFYlGjVCrJcrmUx+Mhh8NB1uu1PJ9Pud1uslgspFwuS6VScSJg8X6/K2iz2VQQolAoSBAEmjSZTKTT6WjCbDaT4XAo1WpV17+CXQHr9XrKZrPZyOl00sTYhEgkEom494GWC8h8Plc2AK9WK2XZbrd17fV6yfV6VRA2/Xw+st1uZTQa6XsIKWA6nZZsNivdbldarZaWDQjPrOXzeRkMBlomSQDyDhKNRkNqtZr0+/0//QDkA9MN4FwuFwYfkAzTTCYTvuO5Xq+rvmjJPQ0NS97tdrpbVBe7wozE6DsYUzblco8DcEcIyA7YxJIseCbJ1iwJEPRDHko/Ho8uIJoZC7+D0+nU2SzKEJ2p4IshHkPDOEC67gOaxWBHLpvSwBCQD6xLvoZMhL8Gw8vloiTw6/l8jnryr+Sw7R5DEnxAbMJAcIUdgE7JLPwqGTZ+yTQK7Vhjw6+mIK7fFAs6af6LAtpMM11fgIj7C5DN7KCIehNApsTG1gGEup0eflOQI65kmMGee8c2fIToxsLXMGqpKENsw+zjEEzujB5eY1p+MfQBqQhAmxSOP6dk9Phfyb6GNilcqYCjzmFoI/TLNj4gmwDClYCtA4gW7/c7FpBJiSuZQ5VmYWymxpkUhKWEuGM9zjboBis6DVP+SaGG3HB4mjX8YM3fxH5iGN4OW/vmH4dUD78au0JuAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Before Reassignment&quot;
        title=&quot;&quot;
        src=&quot;/static/c594a1422f5ed095d1fe246ce0407d75/b53cf/before_reassignment.png&quot;
        srcset=&quot;/static/c594a1422f5ed095d1fe246ce0407d75/12f09/before_reassignment.png 148w,
/static/c594a1422f5ed095d1fe246ce0407d75/e4a3f/before_reassignment.png 295w,
/static/c594a1422f5ed095d1fe246ce0407d75/b53cf/before_reassignment.png 319w&quot;
        sizes=&quot;(max-width: 319px) 100vw, 319px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;After running partition reassignment:&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 319px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/4bac8ceb2c069dc204f5714c5b4114cb/b53cf/after_reassignment.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 114.1891891891892%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAXCAYAAAALHW+jAAAACXBIWXMAAAsTAAALEwEAmpwYAAACMklEQVR42o2V547qQAyFQ++X3kvoXawEEkKC938t73zmTpRkEml/WCSe+PjYPmO8Wq0mpVJJisWiZDIZtWw2q7+e50mhUNAzfvP5vD5jPHPu2Ov1kvf7LcfjMXJgAZfLpfDNZrORxWIhz+dTbrebjMfjyHeBXS4X8X1fP57NZvq82+2k1+slMoA9lsgOa7VacjqdFGg+n8tqtVI2k8lEP6hWq0JbOp2ONBoN9fX7fWk2m9Jut6VSqeg3tOQ/qKfMwkwB5Z1yKO1+v2vZ1+tVgzj/fD6y3+815vF4aJIAkBIsE6xer2tmAMmMzzKxQyuXy4HhDw3pm5Eg22QbxPtwOAzKCftg1O12g4pCA/pOkkxJTaavof6o2X4zOMDO53MUkEzxIGvr9Vp1GvZNp1MdGnEMEFVEAEejkRNkDRbxZJSJUTrmAJItDRAFxG8FJdMmGFL64XBwGTLFJECyx5NZQNgjHSTmMEwbSnhg4evIMADkmYsRAUS8aSXTo/h9pX/EDAYDHZpTMn1KAyQwqYcYgEzZkQ3OtJJpRxogTK10IoBcfHZcEiCai5/ZoXBFExlSVpqwk0qGmb0pqMDu0gBwu90GdzluBOdyOccXXneOsHGyMf56l+1CsMsBLTqAdtfFJUKvbA/DOsSQ1Fc2R1c2m806BdA3CihFAU25K+Pv93sGkG0TAxyNfZnOoF0x/WqYvVdXy2TM6u/OzVD+BT7Pq5qKLobATRb+yYDyH/Rj/GWN/QUFyw2zSjL86gAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;After Reassignment&quot;
        title=&quot;&quot;
        src=&quot;/static/4bac8ceb2c069dc204f5714c5b4114cb/b53cf/after_reassignment.png&quot;
        srcset=&quot;/static/4bac8ceb2c069dc204f5714c5b4114cb/12f09/after_reassignment.png 148w,
/static/4bac8ceb2c069dc204f5714c5b4114cb/e4a3f/after_reassignment.png 295w,
/static/4bac8ceb2c069dc204f5714c5b4114cb/b53cf/after_reassignment.png 319w&quot;
        sizes=&quot;(max-width: 319px) 100vw, 319px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;As you can see, the Kafka controller node readjusted the partitions storage to different brokers. Initially, the Kafka controller node stored Partition 0 on brokers 3, 4 and 5, with 4th as the leader. After the reassignment, it got stored on 1, 5 and 9 with the 5th broker as the leader.&lt;/p&gt;
&lt;p&gt;When adding new brokers, the partition reassignment will ensure that the partitions of topics are balanced across the brokers, including the ones we added as part of scaling. We need to do this on all the topics for optimal load balance across newly added brokers.&lt;/p&gt;
&lt;h2&gt;Execution&lt;/h2&gt;
&lt;p&gt;Now let’s have a quick look at how to go about doing the reassignment.&lt;/p&gt;
&lt;p&gt;The standard Kafka installation has the required script to run the reassignment. It can be found as &lt;code class=&quot;language-text&quot;&gt;bin/kafka-reassign-partitions.sh&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The reassignment is a 3 step process:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;First, we put the list of topics we want to load balance in a JSON file.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;topics&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;topic&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;test-topic&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;version&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;We tell the Kafka cluster to generate a plan for us. It will log to console, the current partition replica assignment and proposed partition reassignment configuration&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;bin/kafka-reassign-partitions.sh &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
--bootstrap-server &lt;span class=&quot;token string&quot;&gt;&quot;my-kafka.example.com&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
--broker-list &lt;span class=&quot;token string&quot;&gt;&quot;1,2,3,4,5,6,7,8,9&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
--topics-to-move-json-file &lt;span class=&quot;token string&quot;&gt;&quot;/root/topics.json&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;token parameter variable&quot;&gt;--zookeeper&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;my-zookeper.example.com&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;token parameter variable&quot;&gt;--generate&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Put the proposed partition reassignment configuration in a JSON file.&lt;/p&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;We execute the assignment on the proposed plan.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;bin/kafka-reassign-partitions.sh &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
--bootstrap-server &lt;span class=&quot;token string&quot;&gt;&quot;my-kafka.example.com&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
--reassignment-json-file &lt;span class=&quot;token string&quot;&gt;&quot;/root/plan.json&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;token parameter variable&quot;&gt;--zookeeper&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;my-zookeper.example.com&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;token parameter variable&quot;&gt;--execute&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The execution time depends on the size of the topic, and it happens asynchronously. You can query the state of this execution by verifying.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;sh&quot;&gt;&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;bin/kafka-reassign-partitions.sh &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
--bootstrap-server &lt;span class=&quot;token string&quot;&gt;&quot;my-kafka.example.com&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
--reassignment-json-file &lt;span class=&quot;token string&quot;&gt;&quot;/root/plan.json&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;token parameter variable&quot;&gt;--zookeeper&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;my-zookeper.example.com&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;token parameter variable&quot;&gt;--verify&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Next steps&lt;/h2&gt;
&lt;p&gt;Doing partition reassignment &lt;em&gt;manually&lt;/em&gt; for a big cluster having thousands of topics is time-consuming. As a next step, we are looking to develop automation to do the partition reassignment with minimal intervention.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[What makes Kafka awesome?]]></title><description><![CDATA[What makes Kafka the backbone of data-driven companies — the design decisions behind its high-throughput, low-latency streaming.]]></description><link>https://absh.dev/what-makes-kafka-awesome/</link><guid isPermaLink="false">https://absh.dev/what-makes-kafka-awesome/</guid><pubDate>Thu, 07 May 2020 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://kafka.apache.org/&quot;&gt;Kafka&lt;/a&gt; is one of those software that are in the foundations of most of the data-driven companies e.g. LinkedIn, Uber, Spotify, Slack, Gojek etc. Kafka solves the problem of high throughput real-time data streaming with minimal latency. It’s flexible and lean architecture has helped companies scale massively without worrying about the quality and latency of the data pipeline. In this blog, we will go through internals of mighty Kafka and will find out what makes Kafka so awesome.&lt;/p&gt;
&lt;p&gt;Let’s begin with a subtle introduction.&lt;/p&gt;
&lt;h2&gt;What is Kafka&lt;/h2&gt;
&lt;p&gt;Kafka is a distributed streaming platform. To quote from the official website,&lt;/p&gt;
&lt;p&gt;A streaming platform has three key capabilities:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Publish and subscribe to streams of records, similar to a message queue or enterprise messaging system.&lt;/li&gt;
&lt;li&gt;Store streams of records in a fault-tolerant durable way.&lt;/li&gt;
&lt;li&gt;Process streams of records as they occur.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Kafka is used to build data pipelines, to move data among systems or applications. Kafka can store the data as well.&lt;/p&gt;
&lt;p&gt;By “data”, we mean any form of raw data or event that needs to be sent across applications or services. For example, user activity events(page views, button clicks, logins, sign ups, likes, comments etc.) or operational metrics such as errors in applications, crashes, system usage etc.&lt;/p&gt;
&lt;p&gt;These raw events are produced by users via their interaction with applications and kafka pipelines can be used to send(stream) them at desired places(applications/services). The consuming applications or services can decide to process this raw data in real time or store it for later processing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why do we need to stream these raw data/events in the first place?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Because this helps data-driven companies make efficient decisions. Companies can use this data to develop internal applications and microservices to improve their search relevance, recommendation systems, and target appropriate ads to customers etc.&lt;/p&gt;
&lt;p&gt;As the use cases grow and companies scale, the user facing applications produce more and more data. Streaming that data to various internal applications and services without compromising scale is the one of the onus of data engineering.&lt;/p&gt;
&lt;p&gt;This data can be of enormous volume. Think of the scale at which social media giants like Facebook, LinkedIn operate and storing all major events done by users on their website and mobile applications. This becomes billions of events every day.&lt;/p&gt;
&lt;p&gt;Streaming such enormous amount of data maintaining minimum latency is no simple job. Kafka is very good at this(we will see how). Moreover, Kafka gives you the ability to perform processing on the stream. One example of such real-time processing is calculating &lt;a href=&quot;https://www.davemanuel.com/investor-dictionary/surge-pricing/&quot;&gt;surge pricing&lt;/a&gt; in apps(like Uber).&lt;/p&gt;
&lt;p&gt;Surge pricing is calculated by taking into consideration the current demand and current supply. These two metrics are used in real-time to calculate how much surge price to add to the trip. For example, Surge price in Uber during rains in the evening.&lt;/p&gt;
&lt;p&gt;The real-time usage of log data brings several challenges. One of which is high throughput. Kafka gives the ability to perform such real-time processing with minimal latency. Kafka is scalable, distributed, supports high throughput, supports real-time consumption of data and provides a &lt;a href=&quot;https://kafka.apache.org/documentation/#api&quot;&gt;rich set of APIs&lt;/a&gt; to make developers life easy.&lt;/p&gt;
&lt;h2&gt;Kafka Architecture&lt;/h2&gt;
&lt;p&gt;Kafka’s architecture is fairly simple. There are a few logical concepts that you need to understand in order to deep dive into Kafka. Kafka runs on &lt;a href=&quot;https://aws.amazon.com/pub-sub-messaging/&quot;&gt;Pub/Sub Model&lt;/a&gt;. Which means, there are entities which produce messages and put in some designated places and there are entities which subscribe to these places.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Topic:&lt;/strong&gt; Stream of messages of a particular type is called a topic. For example, a BookingLog Topic for Uber will keep all messages of trip booking type made on the Uber app.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Producer:&lt;/strong&gt; The entity which produces messages to a topic. The producer application constructs the messages and sends it to Kafka to store that message in a particular topic.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Broker:&lt;/strong&gt; Brokers are servers where the published messages are stored.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Consumer:&lt;/strong&gt; The entity which consumes data that was put in a topic(from the Brokers). Consumers consume messages inside a topic from Broker via a bridge called Message Streams.&lt;/p&gt;
&lt;p&gt;Given these basic terminologies, you would have guessed the basic flow of data in Kafka.&lt;/p&gt;
&lt;p&gt;Producers produce messages(data) in topics. Brokers store those data. Consumers can consume data from these topics.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/86628d1bd5c7b4c66bf5da5effb8d0b0/dd507/1.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 18.91891891891892%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAIAAAABPYjBAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAtklEQVR42m3OzQ7BQBAH8L6Co8SBCOI13byFA4oIEjR1EamrxMWp6G4dNBrbz2m7Ku1aTUgj/vllMoeZzAjskyRlWyMaa/4MwfSfiQZzPYBnyofTbEWwbduHgHcL5JV6qNLH5R/iW1XEhY7W3hN+xYMAACilguu6EIQsiVfYaQz1OjfAtcy34ZojvdhFrZ3JYmrZDiGE7wssl4P12FzD9cWTjiYnn+6Sai7VG6/ymShGqBhR/u0XgtLCvoY9ffcAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Producer-Consumer&quot;
        title=&quot;&quot;
        src=&quot;/static/86628d1bd5c7b4c66bf5da5effb8d0b0/fcda8/1.png&quot;
        srcset=&quot;/static/86628d1bd5c7b4c66bf5da5effb8d0b0/12f09/1.png 148w,
/static/86628d1bd5c7b4c66bf5da5effb8d0b0/e4a3f/1.png 295w,
/static/86628d1bd5c7b4c66bf5da5effb8d0b0/fcda8/1.png 590w,
/static/86628d1bd5c7b4c66bf5da5effb8d0b0/efc66/1.png 885w,
/static/86628d1bd5c7b4c66bf5da5effb8d0b0/c83ae/1.png 1180w,
/static/86628d1bd5c7b4c66bf5da5effb8d0b0/dd507/1.png 1528w&quot;
        sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Topics can be of very high throughput depending on the use cases. BookingsLog alone is a very high throughput topic if you think about Uber’s scale. So to balance the load, topics are further divided into Partitions.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 586px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/2769fcc22eb4e863ad7225e9dfe04ed5/a76f4/2.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 90.54054054054053%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAIAAADUsmlHAAAACXBIWXMAAAsTAAALEwEAmpwYAAACM0lEQVR42pWUy2sTURTGs3cnasVqayvVqpUiFOvKUvRvUHCjKL62PhDcie50IYrWVdHmNY2x0SiCYKFtKk3xUdtOU2yTzMxN0rzmmTTpPO94MzPNYyqEHn4Ml++ew7n33I9xQKgjthsqqtJ1B1qF0uU74fzdMN2Ue2H69gx9f5Ze5WVU6EiVlL4x0OoiDnrI9mZ0eMldb+MPfzGoUoO6A2elTi951Ecd91HHmtHzjmp3E86VgnnyyrEf/2b7AuD0h0R/I6cCANEfqCko5+QYuDCeTq0runlndPWirHGSxhtwolpQ9ATNz/zBF6NkRigJMuQ3dwVJozdUlGwVl1W4xErzjLTQyCIrI+wiI/3Mi5my0VnV4M1QDk3rMEZ1NePIKHXATZ7/lmZEo/McLbZ5iB4/dcIPGqmMx6Qq9r4HnRiJxYrWwFhRHfyU3Pkm1jIS31PHXhfR6k3s84AWJ1ETnfEdw9EboawKoWWSFV4aivCvlixe4tzr5cLTH2sPvuKPpmLP5nJDEaG6i3iOc1HBMAk0jLY1oKbIRVYpF6Cq/M+e0Jr2F1C6PJG9MtnA1anc9e/MtWkaLWriZPbSRPbWdG7V7EwWlV4/aHMTyGcdNjx25RBG7h6JP5nnLHsuc1IXVnmn7tHKS9TT7bMryMX7XcTwX6Fmzxc4PxBMnvucOlvHYDAx8BGgL6IqopwzweTF8Qww7WlOa32LPVNsYXYhguy5xhVt9sxvqKy4aU91+z8DM/4BV1JAiEhFI8kAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Partition&quot;
        title=&quot;&quot;
        src=&quot;/static/2769fcc22eb4e863ad7225e9dfe04ed5/a76f4/2.png&quot;
        srcset=&quot;/static/2769fcc22eb4e863ad7225e9dfe04ed5/12f09/2.png 148w,
/static/2769fcc22eb4e863ad7225e9dfe04ed5/e4a3f/2.png 295w,
/static/2769fcc22eb4e863ad7225e9dfe04ed5/a76f4/2.png 586w&quot;
        sizes=&quot;(max-width: 586px) 100vw, 586px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Each broker stores one or more partitions of various topics.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a4928c83ae4cfed322b03f1484bae121/29007/3.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 41.21621621621622%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAIAAAB2/0i6AAAACXBIWXMAAAsTAAALEwEAmpwYAAABWklEQVR42nVRPUvDUBR9v8HZD9xclK5+DBZcXApujg4dVdDFyc1ZHHRzECGCUg2BUrQoioiCSIdCQWxIY9vYGkNiaPKS95H3vLaLRXo4b7j3vnM57zxEKSWUxf8ITdIFGwAQIjhyAGAtYNAU5CgkVKl29srefsXv8aDi75a9ixpMKNwQUnLRx0TIOJHgDhkenszVRxRzTDGGj3XguGIMHRmbTw6oPoN45c5eLFiZq1am2AYuFdsLeWun5CacIc5YySH591DTvbNKC6i+OYV6eN3EmBAniJdv2um8ldbM2Zw+l9Pn1dqM1tx+6YrN72haa06cmlPnjZTaSqkfKdUaPaltPTtSQGw0gecJGVHWCXEQRZhQcB6BbUKRG8bgYePxa/3eyl7q2cvq2m1j9cE+fPX/BCZ6ISUJl0L0Sogbcc6h2SWPfDdwbZkwKKH/m1f/39i2jTGGEZSc8x8ypKS2+vqX9AAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Topic BookingLog split into 3 partitions &amp;amp; stored on 2 brokers.&quot;
        title=&quot;&quot;
        src=&quot;/static/a4928c83ae4cfed322b03f1484bae121/fcda8/3.png&quot;
        srcset=&quot;/static/a4928c83ae4cfed322b03f1484bae121/12f09/3.png 148w,
/static/a4928c83ae4cfed322b03f1484bae121/e4a3f/3.png 295w,
/static/a4928c83ae4cfed322b03f1484bae121/fcda8/3.png 590w,
/static/a4928c83ae4cfed322b03f1484bae121/efc66/3.png 885w,
/static/a4928c83ae4cfed322b03f1484bae121/c83ae/3.png 1180w,
/static/a4928c83ae4cfed322b03f1484bae121/29007/3.png 1600w&quot;
        sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Partitions of a topic are simply Log files. Think of all the partitions of a topic as a file on the storage system of the Brokers. Depending on the usage, this partition’s(Log file’s) size can vary. It can be very large for some high throughput topics. So it is &lt;a href=&quot;http://man7.org/linux/man-pages/man1/split.1.html&quot;&gt;split&lt;/a&gt; into multiple smaller files, called &lt;strong&gt;Segment Files&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;A partition now logically looks like:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a9db5c497734c6f25476158324c2b5e8/29007/4.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 35.13513513513513%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAIAAACHqfpvAAAACXBIWXMAAAsTAAALEwEAmpwYAAABfklEQVR42hVQWU/CQBjsr/bFFzVC0Jj4C1QOgcQD5SxdrlJooXe7UEoBS7kUlYRAE0Whwc9kH76Zb2ZndgnTmt+EuWicv43znufh9vQqyIZjzbtHeb/fC9LwOsQFI41kRgdYrfWDEQ4YqtABSOSRoePJz+/2/BI9PClsY2B0Xl3323dGZsgWXevZw8Xnwj3yZYpls1jpvs1X09ny4DBhducE23ipcQOIYup9munJ2hgMoWhTkB0SGZIyAjIS5/XWNJXFguRUGCt2J/LiUJRHBMfbOap94s9SxU5TGIIZGh77s1ChwvREZZRIqqeBnKKNoSp47hNy4IICJVxH1NgBUGAQJQcqiLLTFOzntI7bs0LJ5CWA/9uuNYdXwAxiGFR9oqhjAgLTOaxoo1RWr1QtOBkSS6qTJnGZtlCxg0qGKIEBwyoNv8BYgmRDPm7NiNV6U6aNPFJpxnTdzXL5VSi1SEph6tZms33/WFNIJ5HKNvq7nTeZLvNIA7Eg2rud9weO90s5XMknjgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Partition&quot;
        title=&quot;&quot;
        src=&quot;/static/a9db5c497734c6f25476158324c2b5e8/fcda8/4.png&quot;
        srcset=&quot;/static/a9db5c497734c6f25476158324c2b5e8/12f09/4.png 148w,
/static/a9db5c497734c6f25476158324c2b5e8/e4a3f/4.png 295w,
/static/a9db5c497734c6f25476158324c2b5e8/fcda8/4.png 590w,
/static/a9db5c497734c6f25476158324c2b5e8/efc66/4.png 885w,
/static/a9db5c497734c6f25476158324c2b5e8/c83ae/4.png 1180w,
/static/a9db5c497734c6f25476158324c2b5e8/29007/4.png 1600w&quot;
        sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Partition P2 split into 4 segment FilesLet’s say Partition P2 is the set of segment files S1, S2, S3 and S4. Any new message produced to Partition P2 will be written to the last segment file(here, say, S4).&lt;/p&gt;
&lt;p&gt;This is to be noted that, at first the segment file lives in the memory of a broker. After a predefined threshold is reached, then only the segment file is flushed to the disk i.e. written on the secondary storage. There are two such predefined thresholds:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A certain number of messages have been published from the publisher of that partition.&lt;/li&gt;
&lt;li&gt;Certain time has elapsed since last published event.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The messages are only exposed to consumers when they are written to the disk. Messages residing in the memory of Brokers are never exposed.&lt;/p&gt;
&lt;h3&gt;Message format&lt;/h3&gt;
&lt;p&gt;Every message in Kafka is stored with an offset. This is so because the messages can be of varying size. So offset is decided by the size of the messages in the segment files.&lt;/p&gt;
&lt;p&gt;For example, Let’s say for segment file S1, three messages came from the producer which are of size 20bytes, 40 bytes and 20 bytes.&lt;/p&gt;
&lt;p&gt;Then the ordering of message in segment file with offset typically looks like the image below with offset of M1 as 0, offset of M2 as 20 and offset of M3 as 40.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9f139a53e0efd7d5291462bf5344a922/d777c/5.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 27.027027027027025%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAIAAADKYVtkAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAj0lEQVR42mWPawrEIAyEvf8JvYFCQdFqW5/th2G7sDs/wpCZTBJ1f1Brva6LWko5jqMs1AXh53mKAcDHGOodRu69xxiFzzmF7Pv+ptMkN+fcWsP8HZZUBCIJRsNKB/dcIIgZKk7ZpJC999SUEl1kagjh52wxcEVbgNBULNFaW2tlg5xnjLn/wEfbtgmHOOcejnAj7m9jzHUAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;A typical order of messages inside a segment file.&quot;
        title=&quot;&quot;
        src=&quot;/static/9f139a53e0efd7d5291462bf5344a922/fcda8/5.png&quot;
        srcset=&quot;/static/9f139a53e0efd7d5291462bf5344a922/12f09/5.png 148w,
/static/9f139a53e0efd7d5291462bf5344a922/e4a3f/5.png 295w,
/static/9f139a53e0efd7d5291462bf5344a922/fcda8/5.png 590w,
/static/9f139a53e0efd7d5291462bf5344a922/efc66/5.png 885w,
/static/9f139a53e0efd7d5291462bf5344a922/c83ae/5.png 1180w,
/static/9f139a53e0efd7d5291462bf5344a922/d777c/5.png 1555w&quot;
        sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Here you can see the offsets are in increasing order but they are not consecutive.&lt;/p&gt;
&lt;p&gt;Traditionally, message queue systems (such as RabbitMQ) use message ID instead of offset. The message ID does the one-to-one mapping of Message’s location on disk. This allows the user to do random indexing of the messages. Random access to messages is a &lt;a href=&quot;https://www.geeksforgeeks.org/difference-between-seek-time-and-rotational-latency-in-disk-scheduling/&quot;&gt;seek&lt;/a&gt; intensive operation. This is a costly IO operation affecting latency. Kafka avoids using message IDs.&lt;/p&gt;
&lt;p&gt;Offsets are also used as consumers acknowledgements. If a consumer acknowledges an offset x, it is assumed that the consumer has consumed all messages with offset &amp;#x3C; x.&lt;/p&gt;
&lt;h3&gt;Managing partitions on the broker&lt;/h3&gt;
&lt;p&gt;It’s time to understand how brokers store and manage any partition. As we have understood, partitions are a set of segment files. Every segment file starts with some message. Every message is identified with a message offset.&lt;/p&gt;
&lt;p&gt;Brokers store a sorted table in memory which basically keeps the first offset of each segment file.&lt;/p&gt;
&lt;p&gt;Let’s see it this way. Let’s say for partition P1, there are 5 segment files(S1, S2, S3, S4 and S5). The first message in S1 has offset 0. Likewise, first messages of files S2, S3, S4 and S5 have offsets 1000, 2000, 2500 and 2700 respectively.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 306px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/79d1bd16b9ce30ab7ade56a2422aabb0/98b92/6.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 138.51351351351352%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAcCAIAAADuuAg3AAAACXBIWXMAAAsTAAALEwEAmpwYAAACB0lEQVR42pWUya8pQRSH++8XkUgQFkhEJGLFysbGxjwPITE+08UlniEuHt6nK9frST/3W1R31zl1Tp3fqWoplUoNh8NOp9M1YjAYGM73er1isShNJpP7/X673e46mNxsNnqTmJlOp9J4PObter3eFPDJJHHdbvd+v9c4PK3GmcXndrtNJBLn8/ll5n6/T6Tj8fil43Q6XS4X/TzOREQpKZ1Oj0YjBOirEcIIIfVWhCyVSmaCsaM/3/xAMEgmkxQcDocjkUg0Gv2BYJDP52OxmNfr9fv9oVBI6fAvs8m2EeypnPG2c7kcD6T7pUao8kRjReNqtSphIJIIr0SjgtKEMy18tMqk5mw2m5fhGL+ltlCS2BxMn89nt9ttNpvH46Fb76rNAaKw5XI5n88ZF4uFceZKpbJarWaz2YcC1qzX608FH2pwaDabUqPR4AJgXqn5LbP5RmnCmdDtdvulYMRmY/ixbUYzwdDjKiOEQTCHw+FyuZxOp8ViYaQ3QirhwxIWajOLFxaXy2U2VigUarVavV43zoyB2sTenlDY4XDY7XaMe5lPNTi0Wq2H2lQvBHzC51SGfYmXuRqa91Db5GK84v/3mVI5lZlMhl5SmuHv8aVggUAgHo8Hg0Gr1crxFAuM77Mys1hMNrTg5PEP43qKNe/+Sd6qmWZy+jStAvRcyixk9K3qdrt/Ad0AHVzSof4QAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Broker&amp;#39;s in-memory table&quot;
        title=&quot;&quot;
        src=&quot;/static/79d1bd16b9ce30ab7ade56a2422aabb0/98b92/6.png&quot;
        srcset=&quot;/static/79d1bd16b9ce30ab7ade56a2422aabb0/12f09/6.png 148w,
/static/79d1bd16b9ce30ab7ade56a2422aabb0/e4a3f/6.png 295w,
/static/79d1bd16b9ce30ab7ade56a2422aabb0/98b92/6.png 306w&quot;
        sizes=&quot;(max-width: 306px) 100vw, 306px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The broker will store an in-memory table which keeps the offset of the first message in each segment file. Here : 0, 1000, 2000, 2500, 2700&lt;/p&gt;
&lt;p&gt;Table of starting offset of each segment fileAny new message which comes, if stored in S5, won’t affect this table. As soon as, a new segment file appears, this table will be updated with the offset of the first message in the new segment file.&lt;/p&gt;
&lt;p&gt;This table is used to find the segment file which contains the offset asked by the consumer.&lt;/p&gt;
&lt;h3&gt;Consumption of messages&lt;/h3&gt;
&lt;p&gt;Consuming messages is fairly simple. The series of events are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Consumers send a pull request to brokers. In this request, there are two things: Consumers specify the &lt;em&gt;offset to begin with&lt;/em&gt;, and an &lt;em&gt;acceptable number of bytes in the message&lt;/em&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Broker on receiving the offset to begin calculates which segment file has the message with asked offset. Broker searches the above table for the address of the segment file to which the message belongs.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Broker fetches the data from that segment file and sends to the consumer.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The consumer consumes this data.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The consumer sends the next offset in the new pull request.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Efficiency of Kafka&lt;/h3&gt;
&lt;p&gt;Kafka is considered the best real-time streaming platform out there. The efficiency speaks for itself in various benchmarking tests. This efficiency results from several design decisions(rather unconventional).&lt;/p&gt;
&lt;h4&gt;Storage Efficiency&lt;/h4&gt;
&lt;p&gt;The first one is the use of offsets instead of message-ids which saves the cost of seek intensive random accesses. On top of that, keeping an index of the offsets of starting message in each segment file makes consumption from a partition fairly efficient.&lt;/p&gt;
&lt;p&gt;The second unconventional choice that we see in Kafka is avoiding any caching in Kafka Layer. Kafka avoids caching every message in memory at the process level. Instead, it relies on the underlying file system cache. This offers multiple benefits.&lt;/p&gt;
&lt;p&gt;First, it avoids double buffering. Modern operating heavily use main memory for disk caching. A modern OS will happily divert all free memory to disk caching with little performance penalty when the memory is reclaimed. All disk reads and writes will go through this unified cache. So even if a process maintains an in-process cache of the data, this data will likely be duplicated in OS page cache, effectively storing everything twice. Although there could be a debate on when and how to process level caching is useful. Read this &lt;a href=&quot;https://queue.acm.org/detail.cfm?id=1563874&quot;&gt;ACM article&lt;/a&gt; for a thorough analysis.&lt;/p&gt;
&lt;p&gt;Secondly, this cache(the file system cache) will stay warm even if the broker service is restarted, whereas the in-process cache will need to be rebuilt in memory.&lt;/p&gt;
&lt;p&gt;The third benefit is that no cache at process level means, less overhead in Garbage collection, giving efficiency in VM based languages.&lt;/p&gt;
&lt;p&gt;The fourth benefit comes from the use of traditional caching heuristics like write-through cache. Since both consumer and producer access the segment files sequentially, normal caching heuristics present in most OS works fine.&lt;/p&gt;
&lt;h4&gt;Network Efficiency&lt;/h4&gt;
&lt;p&gt;Consumption of messages from the File system involves fetching message from secondary storage and sending all the way to network sockets. This typically involves reading data from the storage to page cache, copying page cache to application buffer, copying application buffer to kernel buffer and finally sending kernel buffer to a socket. This involves 4 data copying calls and 2 system calls.&lt;/p&gt;
&lt;p&gt;Kafka uses the &lt;a href=&quot;http://man7.org/linux/man-pages/man2/sendfile.2.html&quot;&gt;sendfile API&lt;/a&gt; available in Linux which does the same thing in 2 data copying calls and 1 system call. This optimized API results in the fast consumption of data.&lt;/p&gt;
&lt;h4&gt;Broker’s design efficiency&lt;/h4&gt;
&lt;p&gt;Kafka Brokers are stateless. It means that a broker doesn’t know which consumer has consumed till what offset. The consumer itself keeps track of the offset. It reduces overhead from the Broker. But there are cons as well. Deleting messages becomes difficult since the broker doesn’t know if all consumers have consumed that message. For this, Kafka gives a time-based SLA where retention policy can be configured for brokers.&lt;/p&gt;
&lt;p&gt;Brokers being stateless gives a side benefit to consumers to deliberately choose to rewind back to an old offset and re-consume(if the messages have not exceeded retention period, of course). Though, it is a violation of queue but has proved as an essential feature. For example, if consumer application feeding on data from Kafka has some bugs, then the data can be replayed after the bug has been resolved.&lt;/p&gt;
&lt;h3&gt;Distributed Consensus&lt;/h3&gt;
&lt;p&gt;Let’s see how producers and consumers behave in a distributed setting.&lt;/p&gt;
&lt;p&gt;The producers produce data to a topic stored in Brokers and consumers consumer from these topics. Producers need to decide which partition to produce data. A partition can be randomly selected by the producer or by using a partitioning function.&lt;/p&gt;
&lt;p&gt;Consumers to a topic are put in a group called “Consumer Group”. Each message m of Topic T is delivered to only one consumer in the consumer group. At any time, all messages from one single partition are consumed by a single consumer. Different consumer groups independently consume messages from topics. No coordination is required among them. But within a consumer group, coordination is required.&lt;/p&gt;
&lt;p&gt;The leader inside a consumer group is decided by a separate entity called &lt;a href=&quot;https://zookeeper.apache.org/&quot;&gt;Zookeeper&lt;/a&gt;. Kafka uses Zookeeper as a configuration store.
Zookeeper does these 3 things for Kafka.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Detecting the addition and removal of brokers and consumers&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Triggering re-balance process in each consumer when the above event occurs&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Maintaining track of consumed offset of each partition&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Re-balancing, whenever required, is taken care of by zookeeper by running a re-balancing algorithm.&lt;/p&gt;
&lt;p&gt;Kafka uses Zookeeper to maintain 4 types of registries(config stores)for various purposes. Let’s quickly see what each registry stores.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Broker registry:&lt;/strong&gt; Stores information about brokers. It stores the host and port of the broker and the set of topics and partitions stored in the broker.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Consumer Registry:&lt;/strong&gt; Stores consumer group of each consumer and set of topics each consumer is subscribing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ownership Registry:&lt;/strong&gt; Stores one path for every subscribed partition and the id of the consumer currently consuming for this
partition. This consumer is said to own the partition.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Offset Registry:&lt;/strong&gt; Stores for each partition, the offset of the last consumed message.&lt;/p&gt;
&lt;p&gt;The first three registries are &lt;strong&gt;ephemeral&lt;/strong&gt; i,e. if the creating client is gone, these path created in corresponding registries is removed from by zookeeper server. Offset registry is &lt;strong&gt;persistent&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Each consumer has a watcher on both Consumer registry and Broker registry. Whenever broker set changes or consumer group changes or the startup of a consumer happens the watcher notifies consumers to initiate a re-balancing algorithm process. This is to determine new subsets of partitions it should subscribe from. To learn more about the re-balancing algorithm, read this manual.&lt;/p&gt;
&lt;h3&gt;Delivery Guarantee&lt;/h3&gt;
&lt;p&gt;Kafka only guarantees at least once delivery. By design, they didn’t want to go for the exactly-once guarantee, because that would have retired efforts in two-phase commits which were not necessary requirement for Kafka.&lt;/p&gt;
&lt;p&gt;Kafka also doesn’t guarantee to order of the messages. Since messages go to different partitions, can be consumed from different partitions as per the consumers want. So there is no inherent ordering of messages by design in Kafka. Consumers itself will have to take care of the ordering of the messages.&lt;/p&gt;
&lt;p&gt;Kafka also takes care of the data corruption by giving Cyclic redundancy check at the message level. This allows you to check network error.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;If you have been following up this far, thanks for reading. This blog is inspired by the &lt;a href=&quot;https://www.microsoft.com/en-us/research/wp-content/uploads/2017/09/Kafka.pdf&quot;&gt;Kafka white paper&lt;/a&gt;. I would suggest the readers explore &lt;a href=&quot;https://cwiki.apache.org/confluence/display/KAFKA/Kafka+papers+and+presentations&quot;&gt;this wiki&lt;/a&gt; to learn some more detailed concepts about Kafka.&lt;/p&gt;
&lt;p&gt;Feel free to comment below your thoughts or connect with me on &lt;a href=&quot;https://twitter.com/whoAbhishekSah&quot;&gt;Twitter&lt;/a&gt; for any conversations on this blog.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[No Silver Bullets indeed!]]></title><description><![CDATA[Revisiting Fred Brooks' classic 'No Silver Bullet' — essence vs accident in software engineering, decades later.]]></description><link>https://absh.dev/no-silver-buttes-indeed/</link><guid isPermaLink="false">https://absh.dev/no-silver-buttes-indeed/</guid><pubDate>Sun, 08 Mar 2020 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;This week I spent some time finding and reading about classical textbooks and papers in software engineering so far. This blog post is about one of those highly esteemed papers written, titled &lt;a href=&quot;http://worrydream.com/refs/Brooks-NoSilverBullet.pdf&quot;&gt;“No Silver Bullet — Essence and Accident in Software Engineering”&lt;/a&gt; by Fredrick P. Brooks. This paper was written and published in 1986. Even after 34 years, this paper is widely discussed in the field of software engineering.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/84ab8a1e9b513d4f64d73c5d791fecce/a2510/1.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 135.8108108108108%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAbABQDASIAAhEBAxEB/8QAGAABAQEBAQAAAAAAAAAAAAAAAAECAwT/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/aAAwDAQACEAMQAAAB99RMOogMA//EABkQAAIDAQAAAAAAAAAAAAAAAAABAhARIP/aAAgBAQABBQIwcR9uv//EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQMBAT8BH//EABQRAQAAAAAAAAAAAAAAAAAAACD/2gAIAQIBAT8BH//EABQQAQAAAAAAAAAAAAAAAAAAADD/2gAIAQEABj8CT//EABoQAQACAwEAAAAAAAAAAAAAAAEAECExQXH/2gAIAQEAAT8h7yeCAs2oY7syQV//2gAMAwEAAgADAAAAEB8w/wD/xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAEDAQE/EB//xAAUEQEAAAAAAAAAAAAAAAAAAAAg/9oACAECAQE/EB//xAAbEAADAQEBAQEAAAAAAAAAAAAAAREhMWGRQf/aAAgBAQABPxBp4jL2jhZp6VTRji5+lzi+CmtaN3o5H34IVIbVcEThIf/Z&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;No Silver Bullets paper, image courtesy: Myself!&quot;
        title=&quot;&quot;
        src=&quot;/static/84ab8a1e9b513d4f64d73c5d791fecce/1c72d/1.jpg&quot;
        srcset=&quot;/static/84ab8a1e9b513d4f64d73c5d791fecce/a80bd/1.jpg 148w,
/static/84ab8a1e9b513d4f64d73c5d791fecce/1c91a/1.jpg 295w,
/static/84ab8a1e9b513d4f64d73c5d791fecce/1c72d/1.jpg 590w,
/static/84ab8a1e9b513d4f64d73c5d791fecce/a8a14/1.jpg 885w,
/static/84ab8a1e9b513d4f64d73c5d791fecce/a2510/1.jpg 1000w&quot;
        sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;As we have known that in the field of hardware(electronics) there has been a two-fold increase in the number of transistors that can be put on an IC every two years. This is referred to as &lt;a href=&quot;https://en.wikipedia.org/wiki/Moore%27s_law&quot;&gt;Moore’s law&lt;/a&gt;. Fredrick argues that there is no single development technique which by itself promises even one order-of-magnitude improvement within a decade in productivity or in reliability or in simplicity in software engineering. There is neither any management technique as well which promises such improvements. There are no silver bullets in software engineering.&lt;/p&gt;
&lt;p&gt;But why is this case with software engineering? Fredrick analyses the intrinsic nature of the whole process that is “Software engineering” to find out why it is so. He finds two kinds of difficulties or complexities in software engineering. One is Essential difficulty and another is Accidental difficulty. Let’s go through each one by one.&lt;/p&gt;
&lt;h3&gt;Essential difficulties&lt;/h3&gt;
&lt;p&gt;Fredrick goes on writing that the very nature of software is the root cause that there is no silver bullet. And this only makes it unlikely that there will be any silver bullet in future. We cannot expect any silver bullets in this field of ours. The essence of complexity in software engineering lies in data sets, relationships among those data sets, algorithms and invocation of functions. But the hard part of building software lies in specification, design and testing. There will always be syntax errors and need for improvement in the code, but they are tiny things compared to the bigger things like unclear specification, design flaws and loose testing.&lt;/p&gt;
&lt;p&gt;Software is perhaps much more complex than any other human construct because no two parts are alike. In this aspect, Software shows acute differences from hardware, automobiles or buildings etc. Scaling-up a Software entity is not merely a repetition of the same elements in a larger size rather it is an increase in the number of different elements. The elements, in most cases, interact with each other in some non-linear fashion, and the complexity of the whole Software increase in much more than linearly.&lt;/p&gt;
&lt;p&gt;From this complexity, comes the difficulty of communication among team members, leading to product flaws, cost overrun, schedule delays, the difficulty of enumerating, lesser understanding of all the possible states of the program and from that comes the unreliability.&lt;/p&gt;
&lt;p&gt;Changeability is one other kind of intrinsic difficulty with Software. All the successful software gets changed(evolved) as time passes. As software is found to be useful, people try it in new cases at the edge of, or beyond, the original domain. The vehicle on which software rides(the underlying hardware) is changing at a very fast pace. The software has to live beyond the life of the hardware.&lt;/p&gt;
&lt;h3&gt;Accidental difficulties&lt;/h3&gt;
&lt;p&gt;These are such difficulties which are not inherent to the domain but arise due to the interactions among the stakeholders and processes. There have been some developments in overcoming accidental difficulties in Software Engineering but these alone cannot promise order-of-magnitude improvements. Because they do not solve the inherent difficulties. Let’s go through some of the breakthroughs that solved accidental difficulties to some extent.&lt;/p&gt;
&lt;p&gt;High-level languages are one such example. They have been the most powerful stroke for software productivity, simplicity, reliability and comprehensibility. High-level languages frees a program from much of its accidental complexity by providing abstractions over conceptual constructs. It eliminates a whole level of complexity that was never inherent in the program at all. But at some points, the high-level language becomes a burden, that increases the intellectual tasks of the user those esoteric constructs.&lt;/p&gt;
&lt;p&gt;Unified programming environments like Unix have boosted productivity by integral factors. These integrated programming environments provide integrated libraries, unified file formats and piles and filters.&lt;/p&gt;
&lt;h3&gt;Hopes for Silver bullets&lt;/h3&gt;
&lt;p&gt;Fredrick, next analyses, technical developments that are most often advanced as potential silver bullets. Ada and other programming languages solved some accidental difficulties by its philosophy of modularization and abstract data types. But surely, it will not prove as the silver bullet, as it another high-level programming language. Object-oriented programming looks like one more hope for a silver bullet to Fredrick. But object-oriented programming removes all the accidental difficulties from the expression of design. The complexity of the design itself is essential. And an attack on essential difficulties makes no change whatever in that. Similarly, Fredrick goes on discarding Artificial Intelligence, &lt;a href=&quot;https://en.wikipedia.org/wiki/Expert_system&quot;&gt;Expert Systems&lt;/a&gt;, Automatic programming, Graphical Programming, Program Verification, Environment &amp;#x26; Tools and workstations as silver bullets in Software Engineering.&lt;/p&gt;
&lt;h3&gt;Attacks on Conceptual Essence&lt;/h3&gt;
&lt;p&gt;Fredrick goes on analysing some promising attacks on the essential complexity of Software. Buying software is one such approach instead of building it, as more and more vendors offer more and better software products for a variety of applications. They generally come with better documentation and somewhat better maintained than homegrown software. Better software has always shown remarkable improvement in productivity, for example, personal computers with well-generalized writing, drawing file and spreadsheet programs.&lt;/p&gt;
&lt;p&gt;Precisely deciding what to build is the hardest single part of building software. Most of the times client also doesn’t know what they want to bet built. So in planning any software activity, it is necessary to allow for an extensive iteration between the client and the designer as part of the system definition. It is really impossible for clients to specify completely, precisely and correctly the exact requirements of a modern software product before having built and tried some versions of the product they are specifying. &lt;a href=&quot;https://devsquad.com/blog/what-is-rapid-prototyping-and-why-is-it-used-in-development/&quot;&gt;Rapid prototyping&lt;/a&gt; has been a very promising attack on this essence.&lt;/p&gt;
&lt;p&gt;Software systems must be grown, not built. It should be grown in incremental development. The system should first be made to run, even though it does nothing useful except call some dummy subprograms. Then bit-by-bit it should be evolved into complex modules and function calls. Iterative development has shown dramatic results in projects. This also helps in building morale. Enthusiasm jumps when there is a running system, even a simple one.&lt;/p&gt;
&lt;p&gt;The last piece of this analysis concerns with people. Fredrick argues, that we can get good designs by following good practices instead of poor ones. &lt;em&gt;Good design practices can be taught&lt;/em&gt;. Unix, Pascal, Smalltalk, Fortran are some examples of software that are the products of one or a few great designers. Fredrick advocates that the most important single effort we can mount is to develop ways to grow great designers. No software organisation can ignore this challenge. Good managers are scarce but no scarcer than good designers. Great designers and great managers are both rare. Organizations should spend equal effort in finding and developing great designers as they do on management prospects. For those are the people upon whom the technical excellence of the products will ultimately depend. Fredrick proposes few suggestions to grow great designers. Like systematically identifying top designers as early as possible. The best designers are often not the most experienced. Assign a career mentor to be responsible for the development of the prospect, device and maintain a career development plan for each prospect and provide opportunities for growing designers to interact with and stimulate each other.&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Well, for starters, this paper convinces me of not searching for any silver bullets. Moreover, it gives a better perspective to me at how to look at software, in terms of Essential and Accidental difficulties. This mind shift change actually lowers the burden of thought process in a day to day basis. I shouldn’t fight or worry about the essence of Software. It happens most of the times to us that we get exhausted or frustrated with the problems while developing software. Thinking those difficulties in terms of essential and accidental is a huge thought process change. Complex/hazy requirements, non-linear interactions of different pieces are all inherent to Software. I think this is both the beauty and the curse of this field.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Inspired from the paper, next I have picked the &lt;a href=&quot;https://blog.codinghorror.com/recommended-reading-for-developers/&quot;&gt;Mythical Man-Month (anniversary edition)&lt;/a&gt;. A detailed blog on the book would follow later. Thanks for reading. Feel free to connect with me on &lt;a href=&quot;https://twitter.com/whoAbhishekSah&quot;&gt;Twitter&lt;/a&gt; for any conversations on this blog.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Books I read in 2019]]></title><description><![CDATA[A roundup of the books that shaped my thinking in 2019, with notes on what I took from each.]]></description><link>https://absh.dev/books-i-read-in-2019/</link><guid isPermaLink="false">https://absh.dev/books-i-read-in-2019/</guid><pubDate>Tue, 14 Jan 2020 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;I wanted to talk about the books I have read in 2019. I feel like I have learnt some good things from books and I feel like I should share with everyone. So here we go!&lt;/p&gt;
&lt;p&gt;It has been a good year since I started realizing the power of reading books. I spend most of my times with books related to the things I like viz: software development, astronomy, personal finances among many other things. A detailed list of books that I read in 2019 is here on &lt;a href=&quot;https://www.goodreads.com/user/year_in_books/2019/74087763&quot;&gt;Goodreads&lt;/a&gt;. This blog talks about the books I loved the most and recommend everyone to read once if interested in that genre.&lt;/p&gt;
&lt;p&gt;I will mostly talk about the books of Personal Finances and Astronomy. Let’s start with Personal Finances first.&lt;/p&gt;
&lt;h3&gt;Personal Finances&lt;/h3&gt;
&lt;p&gt;Personal finances got my interest at the beginning of my senior year of college. I had taken a course called Financial Engineering and later on, I started liking the various concepts of finances(like options, trading, hedge fund market etc.). Later on, I read some books on personal finances. My personal favourite, on this topic, has been &lt;strong&gt;Rich Dad Poor Dad&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;This book changed my vision of how I think about finances and expenses. This book teaches us the major difference between the Rich and Poor, which is the mindset (and money follows that). This book teaches you the mindset required to be rich. The author presents very good real-world examples which support his arguments. A simple example, taken from the book:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The poor and the middle class work for money. The rich have money work for them.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The book is filled with much such great advice which will change the way you look at your finances. Personally I agreed with almost all of the advice that was given in the book to be rich. But it is not an easy path to create wealth. The way we have been living our lives has conditioned us to the exact opposite(at least for me). So it requires a lot of discipline, and frequent self-introspection to ensure I am applying those learnings from this book.&lt;/p&gt;
&lt;p&gt;I would highly recommend this book for anyone who has never cared about personal finances(literally, complete beginners) and who are struggling to get their financial situations right.&lt;/p&gt;
&lt;p&gt;I was very keen to implement those learnings from Rich Dad Poor Dad, and so I started looking for books which teach what all financial instruments are available in India and their analysis. So the next book, I read was What Every Indian Should Know Before Investing. This is a great book for complete beginners who are not very familiar with the various financial instruments like Fixed Deposits(FD), Recurring Deposits(RD), Mutual Funds, Equities, and their tax implications. After gathering sufficient information, I started investing in multiple instruments. These books gave me enough exposure and confidence to take good care of my finances. Highly recommended!&lt;/p&gt;
&lt;h3&gt;Astronomy&lt;/h3&gt;
&lt;p&gt;Astronomy intrigues me on a very different level. I like to read about the discoveries and happening of the outer worlds. This subject is just so delightful to read about on any given day. Cosmos by Carl Sagan is such a treat for astronomy lovers. In a 384 pages long journey with Carl, you will discover many worlds like Venus, Mars, Jupiter, Saturn. You will find out what these worlds are made of, the possibility of intelligent lives on these worlds and their probable fate. I love the way he presents information about these world by logical inferences which are so simple to understand, they don’t require a very sound understanding of the Physical Sciences. After reading the books by Carl, I would say he is the best teacher I have got in astronomy. His books always spark curiosity in me to explore the unknown.&lt;/p&gt;
&lt;p&gt;Apart from these two subjects, I read a few books targeted on the contemporary political scenarios of India. The free Voice by Ravish Kumar is a good example which presents various incidents happened around India which reflect how India as a nation is changing democratically and culturally.&lt;/p&gt;
&lt;p&gt;So, overall it has been a good year. I learnt a lot of new things and applied them in real life. Looking forward to an even great year ahead!&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Thanks for reading. Feel free to connect with me on Twitter for any conversations on this.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Object.assign() in JavaScript]]></title><description><![CDATA[How Object.assign() copies enumerable properties between objects in JavaScript, with the gotchas to watch for.]]></description><link>https://absh.dev/Object.assign()-in-JavaScript/</link><guid isPermaLink="false">https://absh.dev/Object.assign()-in-JavaScript/</guid><pubDate>Tue, 24 Dec 2019 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;Object.assign() is used to assign all enumerable properties of one or more source objects to some target object.&lt;/p&gt;
&lt;p&gt;Object.assign() is a very useful and very frequently used method in JavaScript. It takes a target object and one or more source object. It modifies the target object with several usages listed below. Source objects are never modified.&lt;/p&gt;
&lt;h3&gt;Copying enumerable properties&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; x &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; y &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; z &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;y&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;z&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; y&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//{ a: 5 } { a: 5 } { a: 5 }&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Let’s see the usage of this method from some examples:&lt;/p&gt;
&lt;p&gt;As we can see, the variable &lt;code class=&quot;language-text&quot;&gt;x&lt;/code&gt; has an enumerable property a which is equal to 5. By doing an &lt;code class=&quot;language-text&quot;&gt;Object.assign(y, x)&lt;/code&gt; those enumerable properties of &lt;code class=&quot;language-text&quot;&gt;x&lt;/code&gt; will be copied to &lt;code class=&quot;language-text&quot;&gt;y&lt;/code&gt;. The copied object will be returned by this method.&lt;/p&gt;
&lt;h3&gt;Overwriting common enumerable properties&lt;/h3&gt;
&lt;p&gt;Common properties of the target object will be overridden by values in source objects. Let’s see the usage of this method from some examples:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; x &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;7&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; y &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; z &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;y&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;z&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; y&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//{ a: 5, b: 7 } { a: 5, b: 7 } { a: 5, b: 7 }&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As we can see, the variable &lt;code class=&quot;language-text&quot;&gt;x&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;y&lt;/code&gt; have common property a. By doing an &lt;code class=&quot;language-text&quot;&gt;Object.assign(y, x)&lt;/code&gt; the common enumerable properties of the target get overridden by that of the source.&lt;/p&gt;
&lt;h3&gt;Merging objects&lt;/h3&gt;
&lt;p&gt;This method can also be used to create new objects by merging two or more variables. For example:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; x &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; y &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; z &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; v &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; w &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; y&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; z&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; v&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;w&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; y&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; z&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; v&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// { a: 1, b: 2, c: 3, d: 4 } { a: 1, b: 2, c: 3, d: 4 } { b: 2 } { c: 3 } { d: 4 }&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Although there is nothing different in the working on this method in this example, if you see it from the perspective of utility, you can use this method for merging multiple objects in one object. Here we are merging y, z, v, into a target object x.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;Object.assign()&lt;/code&gt; is very important method finding it’s usage at various places.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Core value of core engineering]]></title><description><![CDATA[The core engineering principles taught in Gojek's bootcamp, and why they hold up in real production work.]]></description><link>https://absh.dev/core-values-of-core-engineering/</link><guid isPermaLink="false">https://absh.dev/core-values-of-core-engineering/</guid><pubDate>Mon, 09 Sep 2019 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;At Gojek, fresh graduates go through a rigorous two months Bootcamp programme. The Bootcamp is structured in several modules. The first of these modules are core engineering. Here I will introduce you to some of the main themes of the core engineering module. Let’s dive in!&lt;/p&gt;
&lt;h3&gt;How to make decisions, quickly!&lt;/h3&gt;
&lt;p&gt;An engineer is supposed to make many decisions every day. Decisions can be of many aspects ranging from how to name a variable to what should be the road map of the team for the upcoming days and months. This is a problem that we face several times a day. You can’t just take any decisions. You have to have some reasoning behind it. You must do some analysis of alternatives. Sometimes you have to convince your teammates about your approaches to a problem(what decision you took)! Here come the issues when you have to explain everyone and convince them.&lt;/p&gt;
&lt;p&gt;You need to develop many soft skills. No matter how smart you are or how great a programmer you are, but if you cannot communicate properly and if you cannot explain complex things to people in simple English, you are going to find it very hard to deal with people, every day.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; &quot;
    &gt;
      &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAPABQDASIAAhEBAxEB/8QAGAAAAgMAAAAAAAAAAAAAAAAAAAQBAgX/xAAWAQEBAQAAAAAAAAAAAAAAAAADAAH/2gAMAwEAAhADEAAAAXqIzk+Zoh//xAAZEAACAwEAAAAAAAAAAAAAAAACEQABISL/2gAIAQEAAQUC2rPJzCbIiQtf/8QAGBEAAgMAAAAAAAAAAAAAAAAAAAEREhP/2gAIAQMBAT8BtBsj/8QAFhEBAQEAAAAAAAAAAAAAAAAAABIT/9oACAECAQE/AZZP/8QAGhAAAgMBAQAAAAAAAAAAAAAAAAEREjEyUf/aAAgBAQAGPwLqSzNJs0emn//EABwQAAIDAAMBAAAAAAAAAAAAAAABESExYXGBof/aAAgBAQABPyFQFRAk0xJYcJ+D5JioxvvCWVO2z//aAAwDAQACAAMAAAAQLN//xAAXEQEBAQEAAAAAAAAAAAAAAAABABEx/9oACAEDAQE/EEdMi5f/xAAWEQEBAQAAAAAAAAAAAAAAAAABADH/2gAIAQIBAT8QG5CS/8QAHBABAQABBQEAAAAAAAAAAAAAAREAITFBYaGB/9oACAEBAAE/EFudFoKyU98y2ABJ3lIXPLhSUiJoDfjW94QhG2Kg+Fjj3rVUq8z/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;IMAGE&quot;
        title=&quot;&quot;
        src=&quot;/static/1c60f13e5be64a32b7b8b91e832e2d78/1c72d/1.jpg&quot;
        srcset=&quot;/static/1c60f13e5be64a32b7b8b91e832e2d78/a80bd/1.jpg 148w,
/static/1c60f13e5be64a32b7b8b91e832e2d78/1c91a/1.jpg 295w,
/static/1c60f13e5be64a32b7b8b91e832e2d78/1c72d/1.jpg 590w,
/static/1c60f13e5be64a32b7b8b91e832e2d78/a8a14/1.jpg 885w,
/static/1c60f13e5be64a32b7b8b91e832e2d78/dde82/1.jpg 910w&quot;
        sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
    &lt;/span&gt;
Photo by &lt;a href=&quot;https://unsplash.com/@austindistel?utm_source=medium&amp;#x26;utm_medium=referral&quot;&gt;Austin Distel&lt;/a&gt; on Unsplash&lt;/p&gt;
&lt;h3&gt;Time is the most important factor&lt;/h3&gt;
&lt;p&gt;Time is one of the most valuable resources for everyone in the office. So if you are making a conversation in your team, you need to be good enough to express your thoughts clearly and quickly. You need to learn how to speak clearly. In the Bootcamp, we go through a rigorous speaking drill where we practice how to be a good speaker and a good listener. Here I have learned how to convey your thoughts to your audience. An engineer has to deal with a wide range of audience working across different teams. For example, at Gojek, a product engineer may need to talk to Product Managers, Tech Leads, other engineers, business intelligence team, operations team, system engineers, peoples partner etc. There is a different level of engagement needed for different people. You need to put your words differently to be able to convey your message correctly and quickly.&lt;/p&gt;
&lt;h3&gt;Be fallacy free&lt;/h3&gt;
&lt;p&gt;Most of us fall in the trap of logical fallacies in our arguments. In the Bootcamp, we practice rigorously in speaking drill to make our arguments free from &lt;a href=&quot;https://owl.purdue.edu/owl/general_writing/academic_writing/logic_in_argumentative_writing/fallacies.html&quot;&gt;logical fallacies&lt;/a&gt;. We practice hard to inculcate the habit of making fallacy free arguments to sound convincing. Developing such speaking habits makes us reach an agreement in our arguments.&lt;/p&gt;
&lt;p&gt;Of course, you will need your technical expertise in making decisions on a daily basis. But it’s not just about being technically sound, to be a great software engineer. It’s about those aspects of software development which are not taught in any university, the soft skills.&lt;/p&gt;
&lt;h3&gt;Baby Steps&lt;/h3&gt;
&lt;p&gt;The baby steps are about taking small steps to build the solution of a complex problem iteratively. Most of the time you don’t know prior to solving the problem what the solution is going to look like. In complex engineering areas, such as ours, most of the time we can’t say for sure for example how the service or app will look like in the end. There are many variables.&lt;/p&gt;
&lt;p&gt;For example, the customer requirements are not clear initially or the scale is unknown or you are not sure about the approach etc. Baby steps help in these cases. In the core engineering Bootcamp, we develop the habit of taking baby steps in solving some problem because that way we will be able to solve some immediate problem which will, in turn, help you solve the bigger problem. This way you will do frequent commits. Baby steps should be visible on your commit history and not only on pen and paper. Test-driven development helps you take baby steps as you start seeing the bigger problem in smaller chunks of testable behaviours.&lt;/p&gt;
&lt;h3&gt;Being a team player&lt;/h3&gt;
&lt;p&gt;You are not going to work alone. You will always work in a team. We learn the principles of how to work in a team. In the Bootcamp, we also have a team of boot campers. We learn the process required to work effectively in a team. It introduces us to the most basic things in a team, for example, conventions, wiki, communication channel, stand up meetings, timings, objectives, limitations, approachability etc. What tools your team uses, what conventions you follow should be properly documented. The wiki should be the ultimate source of truth for every team member.&lt;/p&gt;
&lt;p&gt;You won’t always write perfect code. No one writes perfect code. You will create technical debt and you cannot escape it. You take the decision that is best for the team and product. As an engineer, you have to decide what technical debts you can afford as a team. Some times you have to do the dirty work(e.g. not refactoring that code smell that you spotted) but you have to know how to repay as a team. By being a good team member is not only about being punctual or just doing your work. It is more than that. You should know what mistakes can you make and what mistakes you can afford. You need to be good at striking a balance between quality and delivering value.&lt;/p&gt;
&lt;p&gt;All of this can be cultivated when you have a belief in the processes that your team follows.&lt;/p&gt;
&lt;p&gt;Complex systems are such systems where output is not proportional to the input. Software is a complex system. The success of your product does not depend proportionally on the number of features you deliver. All of these above themes that I discussed above, help you chunk down the problem in smaller manageable batch sizes. In the hope that you deliver one feature correct and hopefully you will deliver the product right. These aspects help you make the correct software the correct way.&lt;/p&gt;
&lt;p&gt;Thanks for reading. Feel free to connect with me on &lt;a href=&quot;https://twitter.com/whoAbhishekSah&quot;&gt;Twitter&lt;/a&gt; for any conversations on this.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Blockchain is revolutionary!]]></title><description><![CDATA[Why decentralization matters — the problem with central ownership and what blockchain actually changes.]]></description><link>https://absh.dev/blockchain-is-revolutionary!/</link><guid isPermaLink="false">https://absh.dev/blockchain-is-revolutionary!/</guid><pubDate>Sun, 11 Aug 2019 22:12:03 GMT</pubDate><content:encoded>&lt;h3&gt;The problem with central ownership!&lt;/h3&gt;
&lt;p&gt;In today’s era, data is everything. Whoever owns the data has the ultimate power. The central authority to data has started bringing us all kinds of evils such as data theft, data manipulation and unauthorised usage. The centralized world is failing us on data privacy and control.&lt;/p&gt;
&lt;p&gt;The big giants(such as Facebook, Google, Amazon, etc.) are controlling the data and using that data to target us with ads and recommendations. We have seen what are the drastic implications of data misuse because of such central authority. The recent such incident with the application FaceApp. In this &lt;a href=&quot;https://www.sydney.edu.au/news-opinion/news/2019/08/06/is-faceapp-hoarding-our-data-.html&quot;&gt;article&lt;/a&gt;, the University of Sydney describes how Faceapp could be misuse user’s data.&lt;/p&gt;
&lt;h3&gt;How Blockchain can solve this!&lt;/h3&gt;
&lt;p&gt;I think blockchain can solve the problem of trust, security and ownership. At its heart, a blockchain is a record of transactions, like a traditional ledger. These transactions can be any movement of money, goods or secure data — a purchase at a supermarket, for example, or the assignment of a government ID number.&lt;/p&gt;
&lt;p&gt;Blockchain is designed to store information in a way that makes it virtually impossible to add, remove or change data without being detected by other users.
In the centralised internet, transactions are verified by a central authority. Blockchain applications could replace these centralized systems with decentralized ones, where verification comes from the consensus of multiple users. Blockchain usage algorithms based on cryptocurrency to solve these problems.&lt;/p&gt;
&lt;p&gt;Many countries have started using blockchain-based application which provides peer to peer trust, safety, security, no single point of failure and no centralized authority over customer’s data. &lt;a href=&quot;https://instadapp.io/&quot;&gt;InstaDApp&lt;/a&gt; is a good example of how DApps are gaining popularity.
Blockchain will be to businesses what the Internet was to communication. This is going to be the next big thing.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Thanks for reading. Feel free to connect with me on &lt;a href=&quot;https://twitter.com/whoAbhishekSah&quot;&gt;Twitter&lt;/a&gt; for any conversations on this.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[REST vs HTTP]]></title><description><![CDATA[Untangling REST from HTTP — what the REST constraints actually are and how they differ from the protocol underneath.]]></description><link>https://absh.dev/REST-vs-HTTP/</link><guid isPermaLink="false">https://absh.dev/REST-vs-HTTP/</guid><pubDate>Sun, 11 Aug 2019 22:11:03 GMT</pubDate><content:encoded>&lt;p&gt;REST is a pretty common term that you will encounter now and then in web development. REST stands for Representational State Transfer. These are a set of core principles for architecting your software(mostly web services).&lt;/p&gt;
&lt;p&gt;Initially introduced by Roy Fielding in his PhD dissertation, here I will try to explain in brief what those principles are, what it means to be RESTful, it’s different use cases and how HTTP comes into the picture.&lt;/p&gt;
&lt;p&gt;Before getting into REST, we need to look at HTTP quickly. HTTP is an application layer data communication protocol on which(most of) the web works. This protocol defines how messages are formatted and transmitted and what actions web servers and browsers should take in response to various commands.&lt;/p&gt;
&lt;p&gt;HTTP defines a set of methods to perform any action on a given resource. It has a pretty rich set of status codes which makes life way easier for a developer. There are various sophisticated features, also such as authentication, caching, cookies, etc.&lt;/p&gt;
&lt;p&gt;So you see REST is a way of architecting your application and HTTP is the protocol which defines how your application(web services) should be built. One can think of RESTful web services as such services which follow the REST guidelines. HTTP is merely an instantiation of the REST guidelines. Your application built on top of HTTP might be perfectly RESTful, partially REST or kind of 60% REST.&lt;/p&gt;
&lt;p&gt;It is very subjective how close is your software to being perfectly RESTful. Let’s see what should be the common characteristics in all RESTful applications.&lt;/p&gt;
&lt;h3&gt;URIs&lt;/h3&gt;
&lt;p&gt;Every resource(objects) must be uniquely identifiable by a URI. URI stands for Uniform Resource Identifier. Objects could be anything from multimedia objects(images, videos, gifs, etc.) to text files, database rows, etc. Anything that has merits to be identifiable gets a URI. There should be a single consistent naming of the resources. Here the developer should think what the right resources in your application are.&lt;/p&gt;
&lt;h3&gt;Linking URIs&lt;/h3&gt;
&lt;p&gt;These resources should contain links to other resources. Linking them is based on the fact that each has a unique ID. For example, you enter an address in your browser and get a web page(a resource) which links you to other resources. Representational resources link to each other.&lt;/p&gt;
&lt;h3&gt;Uniform Interface&lt;/h3&gt;
&lt;p&gt;REST says you should have the same set of methods for all resources(uniform interface). HTTP makes this specific by its methods &lt;code class=&quot;language-text&quot;&gt;GET&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;PUT&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;POST&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;DELETE&lt;/code&gt;, etc. So you see these methods define what action you could perform on the resources. REST defines these in a very general context. For example GET should be safe, idempotent and cacheable on all the resources. Similarly, there are semantics associated with other methods. It is possible that we could have more such methods, maybe in some other instantiation of REST. That point is REST doesn’t hold you back from having several methods on resources. But it should be consistent over the different type of resources.&lt;/p&gt;
&lt;h3&gt;Interacting with the resources&lt;/h3&gt;
&lt;p&gt;You interact with the resource through their representation only. For example if there is a customer resource identifiable by some URI. If you &lt;code class=&quot;language-text&quot;&gt;GET&lt;/code&gt; it in &lt;code class=&quot;language-text&quot;&gt;text/HTML&lt;/code&gt; format it should return you that format, or if you get it in &lt;code class=&quot;language-text&quot;&gt;application/XML&lt;/code&gt; format, that should also work. RESTful web services allow you to interact with the same resource in different representations.&lt;/p&gt;
&lt;h3&gt;Stateless communication&lt;/h3&gt;
&lt;p&gt;The server does not store any state about the client session. Let’s see it by an example of a shopping cart on an e-commerce website. You can add items to your cart, and you can delete items from your cart. A shopping cart can be implemented in many ways. In one implementation, the server could send a link to your shopping cart. Because it is a resource and identifiable by a URI and also it can be linked to other resources. In another implementation, the shopping cart could be an unnamed client-specific state(session state). So REST says you should go with the first one.&lt;/p&gt;
&lt;h3&gt;Why REST?&lt;/h3&gt;
&lt;p&gt;Well, you have other options also such as GraphQL. If you follow REST guidelines, then you can derive your application-specific interface from the REST specific interface. While doing this, the developer should take care of not violating the semantics associated with the HTTP methods.&lt;/p&gt;
&lt;p&gt;Following REST lets you decouple your application also. Your client could be anything. It can be a browser, a terminal, another apache web server, proxy, etc. The client knows what result that its action is going to produce on the resources if the server is RESTful. For example, let’s say a client is searching for some resource on the server. If you have followed REST, then you have pretty robust GET on all the resources which return a readable representation of that resource. So that will let your client build fast and efficient search algorithms.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Thanks for reading. The &lt;a href=&quot;https://www.se-radio.net/2008/05/episode-98-stefan-tilkov-on-rest/&quot;&gt;SE-Radio Podcast with Stefan Tilkov&lt;/a&gt; inspires this blog. I tried to present the learnings in a brief, concise way. Feel free to connect with me on &lt;a href=&quot;https://twitter.com/whoAbhishekSah&quot;&gt;Twitter&lt;/a&gt; for any conversations on this.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Learnings in Gojek Bootcamp]]></title><description><![CDATA[What I learned in the first week of Gojek's engineering bootcamp.]]></description><link>https://absh.dev/Learnings-in-the-Bootcamp/</link><guid isPermaLink="false">https://absh.dev/Learnings-in-the-Bootcamp/</guid><pubDate>Thu, 08 Aug 2019 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;This blog summarises what all things I have learned in the first week of GOJEK Bootcamp!&lt;/p&gt;
&lt;h3&gt;Communication is hard!&lt;/h3&gt;
&lt;p&gt;Until now, I was not even aware of this simple fact. In Bootcamp, we are learning how to speak clearly. The intent of the communication is that the person listening to you gets what you are trying to say. But different person perceives it differently. So it is your responsibility to make sure that you speak so rigidly yet simply that everyone in the room gets them exact same thing that you are trying to say. Speaking clearly and yet simplistically is hard. It comes with practice. You need to unlearn many things. You need to prepare your facts. You need to have a sound idea into your mind. Your arguments need to be logically consistent.&lt;/p&gt;
&lt;h3&gt;Never love your code!&lt;/h3&gt;
&lt;p&gt;This is also hard to do. But eventually, you will have to develop the habit of not loving your code. Because by doing so, you will lose the ability to evolve it. You will start taking the code reviews very personally.&lt;/p&gt;
&lt;h3&gt;A team has conventions!&lt;/h3&gt;
&lt;p&gt;We have learnt how a professional team functions. It functions on the basis of a structure backed by conventions and automation. The team decides the convention they will use in their projects. The team decides the tools that they will use. They write the conventions in the wiki and treat it as the source of truth. We boot campers are also a team. We are learning how to pick our conventions. How to logically argue and reach a conclusion whenever there is a conflict amongst teammates. To do that, you need to learn how to speak clearly and resolving conflicts without wasting yours and others time.&lt;/p&gt;
&lt;p&gt;Any violation of conventions is not accepted. If code is violating any conventions it gets a &lt;code class=&quot;language-text&quot;&gt;rm -rf&lt;/code&gt; harshly. This thing also serves as a positive learning reinforcement. And also contributes to the fact that you mustn’t love your code.&lt;/p&gt;
&lt;p&gt;To make sure you are not diverting from conventions, you use automation. Automation makes it easy to deal with rules in conventions. Otherwise, there will be inconsistency in the development process which will reflect in design, code and several other decisions.&lt;/p&gt;
&lt;h3&gt;Simple English Modelling&lt;/h3&gt;
&lt;p&gt;You should be able to express your intent in simple plain English. If you can do that, it will make it easier to make decisions quickly, argue rationally and will save everyone’s time. Most of the time we get diverted to other topics from the original topic in a discussion. Simple English modelling also help you not divert from the topic at hand.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;So that’s the learning of week one. Most of this was centred around processes and communication in a professional setting. It is making more and more sense, as we go deeper into the Bootcamp.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Refactoring by Martin Fowler]]></title><description><![CDATA[Key lessons from Martin Fowler's Refactoring — improving code's internal structure without changing its behavior.]]></description><link>https://absh.dev/refactoring-by-martin-fowler/</link><guid isPermaLink="false">https://absh.dev/refactoring-by-martin-fowler/</guid><pubDate>Sun, 04 Aug 2019 22:15:03 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;Refactoring is the process of changing a software system in such a way that it does not alter the external behavior of the code yet improves its internal structure.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Why should you care about refactoring?&lt;/h3&gt;
&lt;p&gt;Design is a pretty sloppy heuristic. No design is final. You learn about your problem by actually solving it bit by bit. You improve the design as you get to know better about what you are trying to achieve. A poorly designed system is hard to change. Hard because it is hard to figure out where the changes are needed. If it is hard to figure out what to change, there is a strong chance that the programmer will make a mistake and introduce bugs. Refactoring will make sure your design is not decaying. It will make your program adaptable to upcoming changes.&lt;/p&gt;
&lt;p&gt;When you find that yesterday’s decision doesn’t make sense today, you change the decision. Now you can do today’s work. Tomorrow, some of your understanding as of today will seem naive, so you’ll change that, too.&lt;/p&gt;
&lt;p&gt;You refactor as you make more sense of your decisions or when the decisions are no longer applicable.&lt;/p&gt;
&lt;h4&gt;A word of caution&lt;/h4&gt;
&lt;p&gt;Before we start refactoring, we should have solid tests. Solid tests only can give you 100% assurance that you didn’t break anything unknowingly while refactoring. While refactoring you must not add any features. You should not add any tests unless you find you missed one earlier. Doing refactoring you only restructure your code. Refactoring mostly changes the interface, so you might have to change tests in order to cope with a change in the interface of the code.&lt;/p&gt;
&lt;p&gt;When you are adding a feature to your program don’t think about refactoring. Don’t think about changing existing code. You should only write your functionalities and get tests to work.&lt;/p&gt;
&lt;h3&gt;Where can I refactor&lt;/h3&gt;
&lt;p&gt;Any place where you see a code smell, it is telling you to refactor. The phrase Code Smell was popularised by Kent Beck. Code smells are characteristic in the source code of a program that possibly indicates a deeper problem(broken window?)&lt;/p&gt;
&lt;p&gt;So you identify the smells and fix them by the best practices of Refactoring. The author has dedicated seven chapters on what sort of smells are and what refactoring technique is best suited to handle them with proper reasoning. We will have a quick overview of smells and refactoring techniques.&lt;/p&gt;
&lt;p&gt;Let’s Refactor!&lt;/p&gt;
&lt;h4&gt;Duplicated Code&lt;/h4&gt;
&lt;p&gt;This is the most common form of smell. You can apply &lt;strong&gt;Extract Method&lt;/strong&gt; technique to make a separate method of the duplicated code. Here you take a clump of code and turns it into its own method. For example:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;printOwing&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt; amount&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;printBanner&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;println &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;name:&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; _name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;println &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;amount&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; amount&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;printBalance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt; amount&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;printBanner&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;println &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;name:&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; _name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;println &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;amount&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; _balance &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; amount&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Applying the extract method technique to this would give us&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;printOwing&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt; amount&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;printBanner&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;printDetails&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;amount&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;printBalance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt; amount&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;printBanner&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;printDetails&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;_balance &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; amount&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;printDetails&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt; amount&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;println &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;name:&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; _name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;out&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;println &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;amount&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; amount&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;There are many applications of the extract method technique. You can use it to make inline methods, replacing temporary variables with query methods.&lt;/p&gt;
&lt;h4&gt;Long Methods&lt;/h4&gt;
&lt;p&gt;A method is supposed to do one thing only. When you find a method that is doing more than one thing, it’s a smell. You need to decompose the method into several methods. You can use the above Extract Method technique or you can use Replace Temp With Query. Let’s see how Replace Temp with Query works.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;calculateTotal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt; basePrice &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; quantity &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; itemPrice&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;basePrice &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; basePrice &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.95&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; basePrice &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.98&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Replacing the temporary variable &lt;code class=&quot;language-text&quot;&gt;basePrice&lt;/code&gt; with its query method with yield.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;calculateTotal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;basePrice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;basePrice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.95&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;basePrice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.98&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;basePrice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; quantity &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; itemPrice&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;Large Class&lt;/h4&gt;
&lt;p&gt;When a class is trying to do too much, it often shows up as too many instance variables. When a class has too many instance variables, duplicated code cannot be far behind. We have seen how to deal with duplicated code. We can also split the class in two. Extract Class, Extract Subclass and Extract Interface are the refactoring techniques used when you encounter this type of smell.&lt;/p&gt;
&lt;h4&gt;Divergent change&lt;/h4&gt;
&lt;p&gt;Ever had a situation when a class has more than one reasons to change. This is a divergent change and a clear violation of SRP(Single Responsibility Principle). When you encounter this type of smell you need to split the class. The fundamental rule of thumb is to put things together that change together.&lt;/p&gt;
&lt;h4&gt;Shotgun Change&lt;/h4&gt;
&lt;p&gt;This is opposite of divergent change. When you make a kind of change(let’s say you changed your database from Postgres to MySQL) and you have to make a lot of little changes to a lot of different classes, then you have encountered this type of smell. Use Move Method, Move Field, Inline class techniques to refactor such broken windows.&lt;/p&gt;
&lt;h4&gt;Feature envy&lt;/h4&gt;
&lt;p&gt;Ever saw a method which calls so many methods from other classes. A method that seems more interested in a class other than the one it actually is in. Use Move Method, Extract Method, to place the method in the correct class.&lt;/p&gt;
&lt;h4&gt;Switch Statements&lt;/h4&gt;
&lt;p&gt;Switch statements on class types are a smell. Most times you see a switch statement you should consider polymorphism. Use Replace Type Code with Subclasses, Replace Type Code with State/Strategy or Replace Conditional with Polymorphism.&lt;/p&gt;
&lt;h4&gt;Middle Man&lt;/h4&gt;
&lt;p&gt;Methods that are only delegating tasks to other classes can be referred to as the middle man. Use remove Middle Man, Inline Method or Replace Delegation with Inheritance.&lt;/p&gt;
&lt;h4&gt;Data Class&lt;/h4&gt;
&lt;p&gt;These are classes that have fields, getting and setting methods for the fields, and nothing else. Such classes are dumb data holders and are almost certainly being manipulated in far too much. Data classes are like children. They are okay as a starting point, but to participate as a grownup object, they need to take some responsibility. Apply encapsulate field, encapsulate collection, remove setting method, move method, extract method.&lt;/p&gt;
&lt;p&gt;There are a lot many refactoring examples in the book but I will conclude here. Thanks for reading. The book contains a lot more information on how to identify areas which need refactoring, how to refactor, what special care must be taken in particular techniques. Give it a read if you haven’t already.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Feel free to connect with me on &lt;a href=&quot;https://twitter.com/whoAbhishekSah&quot;&gt;Twitter&lt;/a&gt; for any conversations on this.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[TDD by Kent Beck]]></title><description><![CDATA[Notes from Kent Beck's Test-Driven Development — using tests to manage fear and drive design.]]></description><link>https://absh.dev/TDD-by-kent-beck/</link><guid isPermaLink="false">https://absh.dev/TDD-by-kent-beck/</guid><pubDate>Sun, 04 Aug 2019 22:14:03 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;Test-driven development (TDD) is a way of managing fear during programming.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;To me, this is the best reason to use TDD. Fear makes you less certain which will clearly affect what sort of code you are going to write(production or test).&lt;/p&gt;
&lt;p&gt;Fear makes you want to communicate less. What sort of effect would it have on your program when you have a constant fear and you can’t express your intent clearly.&lt;/p&gt;
&lt;p&gt;Let’s get started with the introduction first!&lt;/p&gt;
&lt;p&gt;TDD is a way of programming where you write tests first then you write the actual code. Let’s see this with an example.&lt;/p&gt;
&lt;p&gt;You want to write a method which adds two numbers and returns the output. How will you write a code for this function?&lt;/p&gt;
&lt;p&gt;Trick question. In TDD, you don’t think of code. You think of tests first. What set of tests when passed will demonstrate the presence of code we are confident will perform addition of two numbers. Once you have decided those test cases. You write the tests and run them. Obviously, they will fail(might not even compile) because the code is not there. Then you add the minimum amount of code required to make the tests pass. Once you do that you remove the duplication that you have introduced this way.&lt;/p&gt;
&lt;p&gt;A TDD way of writing software looks kind of like this-&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Add a little test&lt;/li&gt;
&lt;li&gt;Run all tests and fail&lt;/li&gt;
&lt;li&gt;Make a little change&lt;/li&gt;
&lt;li&gt;Run the tests and succeed&lt;/li&gt;
&lt;li&gt;Refactor to remove duplication&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This is referred to as the Red-Green-Refactor cycle, the mantra of TDD!&lt;/p&gt;
&lt;p&gt;To make the tests pass, you might have introduced duplication. Duplication is a symptom of a problem called dependency. Duplication most often takes the form of duplicate logic — the same conditional expression appearing in multiple places in the code. Objects are excellent for abstracting away the duplication of logic. By eliminating duplication before we go on to the next test, we maximize our chance of being able to get the next test running with one and only one change. We must get rid of the duplication between the test code and the working code.&lt;/p&gt;
&lt;p&gt;When you are at RED, you quickly want to get to GREEN. How can you do that? Kent Beck tells 3 ways of getting to green quickly. The first two are fairly simple.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Fake It — return a constant from code and gradually replace constants with variables until you have the real code.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Obvious Implementation — type in real implementation.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When everything is going smoothly and you know what to type(Like the addition of two numbers), you put in obvious implementation after obvious implementation (running the tests all the time to ensure that what’s obvious to you is still obvious to the computer).&lt;/p&gt;
&lt;p&gt;As soon as you to get an unexpected red bar(not everything is as obvious as an addition), you back up, shift to faking implementations and refactor to the right code. When your confidence is back, you go back to obvious implementations.&lt;/p&gt;
&lt;p&gt;The third way of getting to green quickly is triangulation.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Triangulation — You Change certain important knowledge in the system and assert that the production code behaves in an accordingly expected manner. Use triangulation when you can’t see a way of eliminating duplication between code and tests. When you cannot think of what axes of variability are you trying to support in your design, make some of them vary and the answer may become clearer.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Doing clean code, you get the benefit of complete code coverage. You are confident about your design decisions. You move quickly. You don’t fear changes because you have tests that will tell you what you need to do next. You get the flexibility of easily refactoring your system because you have solid tests.&lt;/p&gt;
&lt;p&gt;By practice, you learn to take small steps when you don’t know what is your code going to be. You take large steps when you know what your implementations are going to be. The size of your steps is dependent on the scope of the test.&lt;/p&gt;
&lt;p&gt;TDD is very easy in many languages and testing framework such as Ruby with RSpec, even JAVA. With some langauges, TDD is tedious because of the whole Red-Green-Refactor cycle being large to handle in continuation. Experimentation also becomes tedious in such cases.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;So that was a brief moment of ours with the book TDD by Kent Beck. Thanks for reading !!!
Feel free to connect with me on &lt;a href=&quot;https://twitter.com/whoAbhishekSah&quot;&gt;Twitter&lt;/a&gt; for any conversations on this.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Clean code]]></title><description><![CDATA[Notes and takeaways from Robert Martin's Clean Code — the habits that separate professional code from the rest.]]></description><link>https://absh.dev/clean-code/</link><guid isPermaLink="false">https://absh.dev/clean-code/</guid><pubDate>Sun, 04 Aug 2019 22:13:03 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;Writing clean code is what you must do in order to call yourself a professional. There is no reasonable excuse for doing anything less than your best!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The best measurement of code quality is WTFs/minute!&lt;/p&gt;
&lt;p&gt;So let’s get straight into actionable insights on how to reduce the above metric!&lt;/p&gt;
&lt;h3&gt;Meaningful Names&lt;/h3&gt;
&lt;p&gt;Naming is hard! Few things to take care while naming your variables and methods.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use intention revealing names&lt;/li&gt;
&lt;li&gt;Avoid disinformation&lt;/li&gt;
&lt;li&gt;Make meaningful distinctions&lt;/li&gt;
&lt;li&gt;Use pronounceable names&lt;/li&gt;
&lt;li&gt;Use searchable names&lt;/li&gt;
&lt;li&gt;Follow common conventions of the programming language you are using&lt;/li&gt;
&lt;li&gt;Class names should be noun phrases&lt;/li&gt;
&lt;li&gt;Method names should be verb phrases ​&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Functions&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Functions should be small. 4–5 lines is a very good metric. But it is very much specific to your choice of programming language. But the lesser, the better.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Functions should do one thing only.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;There should be only one level of abstraction per function. For example, avoid doing tasks such as inserting employee object to a list and generating the employee payslip in a single function. Because those are a different level of abstractions. Also violates the doing one thing principle.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Avoid switch statements. Switch statements imply your function is doing more than one thing based on some condition. Think of a better way to express your thoughts(Polymorphism maybe?)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Use descriptive names&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;An optimal number of arguments to a function is zero. In the worst case, you can have one or two arguments. But more than two is a strict red alert. If a function has more than two arguments if possible make them a new class (if possible)!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Never use flag arguments. Flag arguments mean your function is doing two things. You need to split your function.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Don’t have side effects associated with calling your function. No hidden-changes. Clearly, do one thing!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Return value objects. Value objects are objects whose instance variables never change once they have been set in the constructor. Value objects biggest advantage is that they save you the trouble of nonorthogonality.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Prefer exception to return error codes. Use try-catch in place of if-else!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;DRY — Don’t Repeat Yourself! Eliminate duplication at all cost.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Comments​&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Best comments are no comments — Comment show your inability to write clean code that expresses your intent.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Formatting&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Indentation ​is most important.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;There shouldn’t be any blank lines in between function. A blank line should mark a logical separation in methods, classes etc.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Instance variables should be placed at the starting of the class on top of methods.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The caller should be above the callee. (But language-specific!)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Focus on readability by giving appropriate vertical density.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Objects and Data Structure​&lt;/h3&gt;
&lt;p&gt;What is the difference between Objects and Data Structure?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Objects hide their data behind abstractions and expose functions that operate on that data.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Objects do not expose data. Rather express their data in abstract terms.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Data Structures expose their data and have no meaningful functions.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Sometimes we need to use data structure, sometimes we need to objects and sometimes both. So we should be able to tell&lt;/p&gt;
&lt;p&gt;With objects, we need to follow the Law of Demeter. Law of Demeter says that a method &lt;em&gt;f&lt;/em&gt; of Class &lt;em&gt;C&lt;/em&gt; should only call methods of&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;C&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;An object created by &lt;em&gt;f&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;An object passed as an argument&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;An object held as an instance variable of &lt;em&gt;C&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Failing law of Demeter implies a function knows many things. Split that function into multiple functions. It could be split into several one line functions that delegate the task to other methods.&lt;/p&gt;
&lt;h3&gt;Error Handling&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Use exceptions rather than error codes ​&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Unit Test&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;TDD is encouraged to be followed.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Tests must be clean (Readability). Don’t treat tests as second class citizens. Your tests should also be written following all the clean code conventions. Test code is as important as production code.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Test coverage should be always high.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Try to have only one assertion per test. If not then try to minimize the number of assertions per test. There should be a single concept per test.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Follow the F.I.R.S.T acronym with unit tests.&lt;/p&gt;
&lt;p&gt;F — Tests should run fast&lt;/p&gt;
&lt;p&gt;I — Tests shouldn’t depend on each other&lt;/p&gt;
&lt;p&gt;R — Tests should be repeatable in any environment&lt;/p&gt;
&lt;p&gt;S — Tests should be self-validating (Test should have a boolean output)&lt;/p&gt;
&lt;p&gt;T — Tests should be written Timely ​&lt;/p&gt;
&lt;h3&gt;Classes​&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Never have a public variable —this implies bad encapsulation. You should protect your classes privacy from the outer world.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Classes should be small. It should be smaller than that.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;One class should have an only responsibility — SRP: Single Responsibility Principle. SRP means a class should have only one reason to change.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Class Name should describe what responsibility it fulfils.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A class should have high Cohesion. Maximum cohesion is achieved when each instance variable is used by each method.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;When classes lose cohesion split them. It means SRP is being violated. Abstractions are getting mixed.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Emergence​&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Systems that are not testable are not verifiable if not verifiable then not deployable. RUN ALL THE TESTS&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Refactoring is something which will help your system emerge. When refactoring you should remove duplication. When you refactor try to be more expressive with your intent. Impose SRP on your modules. Refactor them if SRP is being violated. ​&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Few more tips!&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Building your project(compilation, testing, packaging) should be easy. One should be able to build via a single command or a few commands only. It should bot involve various different checkpoints.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;All test should run with one command.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Avoid multiple languages in one source file. ​&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Avoid Surprises ​&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Never overwrite Safety. This means to take care of your warnings. Simply suppressing your warnings could result in Chernobyl! A disaster.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Eliminate duplication. Follow DRY: Don’t Repeat yourself&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Don’t Mix levels of Abstraction. Follow the law of Demeter.​&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Always remove dead code.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Always maintain consistency ​throughout your project.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Be as expressive as possible.​&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Don’t mix responsibilities.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Make logical dependencies physical.
​&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Prefer Polymorphism to if-else, switch cases ​&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Encapsulate conditionals, boundary conditions, edges cases&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;So that was a brief summary of writing clean code.
Thanks for reading. Feel free to connect with me on &lt;a href=&quot;https://twitter.com/whoAbhishekSah&quot;&gt;Twitter&lt;/a&gt; for any conversations on this.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Pragmatic Programmer]]></title><description><![CDATA[Notes from The Pragmatic Programmer — the tips that actually made me a more effective engineer.]]></description><link>https://absh.dev/pragmatic-programmer/</link><guid isPermaLink="false">https://absh.dev/pragmatic-programmer/</guid><pubDate>Sun, 04 Aug 2019 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;A great book for anyone who wants to become a more effective and productive programmer! This book contains great tips on how to be one!&lt;/p&gt;
&lt;h2&gt;What is pragmatic, you may ask&lt;/h2&gt;
&lt;p&gt;Being pragmatic is all about being skilled in business, caring about your craft. Skill and craft come from experience. It comes from facing failures, learning things and not being afraid of them. Take this to the field of programming.&lt;/p&gt;
&lt;p&gt;What makes a pragmatic programmer?&lt;/p&gt;
&lt;p&gt;Pragmatic programmers are fast adopters. They are always inquisitive — asking lots and lots of questions. They are critical thinkers. They don’t operate on auto-pilot. They are the jack of all trades.&lt;/p&gt;
&lt;p&gt;They take responsibilities. They don’t make lame excuses. When they make a mistake they accept it and provide options. They don’t play the blame game. They don’t live with broken windows(a bad design decision, poor code etc.)&lt;/p&gt;
&lt;p&gt;They invest in their knowledge portfolio every day. They keep on diversifying the knowledge portfolio.&lt;/p&gt;
&lt;h2&gt;A Pragmatic approach&lt;/h2&gt;
&lt;p&gt;There are some ideas which apply to almost every type of software development. A set of ideas and processes which help you build better software.&lt;/p&gt;
&lt;h3&gt;Duplication&lt;/h3&gt;
&lt;p&gt;Duplication is the root cause of all evil. Duplicated logic is hard to deal with. It’s hard to refactor. It is a broken window. We fix all the broken windows. There could be various causes of duplication arising. It could be because of multiple representations of information, comments, language issues. It could have arisen from mistakes in design. Taking shortcuts because of lack of time mostly results in duplications. Remove duplications at all costs. Follow DRY: Don’t Repeat Yourself!&lt;/p&gt;
&lt;h3&gt;Orthogonality&lt;/h3&gt;
&lt;p&gt;Two or more things are orthogonal if a change in one doesn’t affect the other. We want to design components that are self-contained, independent and with a single well-defined purpose. You get a lot of benefits from orthogonality. By promoting orthogonality, you are promoting reusability, which in turn makes you productive. Orthogonality applies to code, to design and even teams. We should maximize orthogonality. Make your module easy to reuse.&lt;/p&gt;
&lt;h3&gt;Reversibility&lt;/h3&gt;
&lt;p&gt;One thing you should always keep in mind is that there are no final decisions. Make your software architecture flexible. Make it easy to accommodate changes. In an object-oriented way of programming, you can achieve this with low coupling, good encapsulation.&lt;/p&gt;
&lt;h3&gt;Tracer Bullets&lt;/h3&gt;
&lt;p&gt;Use tracer bullets to find you want to achieve. The tracer bullet method involves implementing a new application end-to-end to test every layer or component’s interaction. They are preferred to the labour of calculation. Using tracer bullets users get to something working very quickly. Developers build a structure to work in. You have an integration platform. You have something to demonstrate. You have a better feel for progress.&lt;/p&gt;
&lt;h3&gt;Prototype&lt;/h3&gt;
&lt;p&gt;Anything that you are not comfortable with could be prototyped. You can prototype architecture, new functionality, third party tools or UI design. Prototyping could be done in several ways. Prototyping is a learning experience. You build some disposable product, maybe using a totally different technology stack from the original product. You learn what you want to build using prototypes.&lt;/p&gt;
&lt;h2&gt;Basic Tools&lt;/h2&gt;
&lt;p&gt;Every craftsman needs a good set of tool to be productive and carry out the job effectively. For a programmer, these are some of the basic tools that you need to get comfortable with :&lt;/p&gt;
&lt;p&gt;Up your shell game. Shell commands make you highly productive. Compare that to the GUI way of doing it and you will find out. Learn the shell commands and use them. The more you use them, the better you get at it. I have found stream editor(sed) magical. Exploiting the benefits of pipelines, redirection &amp;#x26; composability, you can bring wonders through one single command.&lt;/p&gt;
&lt;p&gt;Use a single editor well. Learn all the shortcuts and keymaps that you need to apply every moment. Vim vs VSCode should not be an argument. Whatever gets your job done and makes you productive, go for it.&lt;/p&gt;
&lt;p&gt;Always use source code control. Git is the best(my opinion 😝 )!&lt;/p&gt;
&lt;p&gt;Develop debugging psychology. Don’t panic when an error popups. While debugging, don’t assume something, rather prove it. This will save you from the trouble of blaming the system for a fault that is likely to be your own (select isn’t broken!).&lt;/p&gt;
&lt;h2&gt;Pragmatic Paranoia&lt;/h2&gt;
&lt;p&gt;Accept the fact that you can’t write perfect software. Because it doesn’t exist. No one writes perfect software. You should be convinced of the fact that you also don’t write perfect software. Pragmatic Programmers code in defences against their own mistakes. Here are some defensive measures that you can take.&lt;/p&gt;
&lt;h3&gt;Design by Contract&lt;/h3&gt;
&lt;p&gt;Bertrand Meyer came up with this elegant concept. Before a function starts, it may have some expectation of the state of the world, and it may be able to make a statement about the state of the world when it concludes. Its a contract between a function and any potential caller:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If all the routine’s preconditions are met by the caller, the routine shall guarantee that all postconditions and invariants will be true when it completes.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;When you design by contract you be strict in what you will accept before you begin and promise as little as possible in return.&lt;/p&gt;
&lt;h3&gt;Crash Early&lt;/h3&gt;
&lt;p&gt;When you follow DBC(Design by Contract), it’s much easier to find and diagnose the problem by crashing early, at the site of the problem. You can easily found out where was the breach of contract. This way you will avoid any surprising results.&lt;/p&gt;
&lt;h3&gt;Assertive Programming&lt;/h3&gt;
&lt;p&gt;The idea is that “if it can’t happen, use assertions to ensure that it won’t.” Assertions check for things that should not happen. Assertions will save the day when you hit an error as silly as array being null, the string being empty, array not sorted etc.&lt;/p&gt;
&lt;h3&gt;Exception Handling&lt;/h3&gt;
&lt;p&gt;Use exceptions but don’t abuse your program in a messy unexceptional way. Use exceptions for exceptional problems. Something being exceptional depends on the context. A FileNotFound could qualify as an exception when you are expecting it should have been there. But when you are not sure whether the file should be there, raising an error seems an appropriate response.&lt;/p&gt;
&lt;h3&gt;Resource Handling&lt;/h3&gt;
&lt;p&gt;Many of the time we forget to close the file we opened to write. This could hit us in pretty serious problems when scaled. Always have a consistent plan to deal with resource allocation and deallocation.&lt;/p&gt;
&lt;h2&gt;Bend or Break&lt;/h2&gt;
&lt;p&gt;Your program should be flexible and adaptable in the face of an uncertain world. How can you achieve that?&lt;/p&gt;
&lt;h3&gt;Law of Demeter&lt;/h3&gt;
&lt;p&gt;Uncle Bob martin describes the law of Demeter as below:&lt;/p&gt;
&lt;p&gt;A method &lt;em&gt;f&lt;/em&gt; of Class &lt;em&gt;C&lt;/em&gt; should only call methods of&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;em&gt;C&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;An object created by &lt;em&gt;f&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;An object passed as an argument&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;An object held as an instance variable of &lt;em&gt;C&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What happens when you don’t follow the law of Demeter?&lt;/p&gt;
&lt;p&gt;You end up having lots of coupled classes. This will restrict your flexibility. Orthogonality will be compromised. So follow the law of Demeter. Organise your code into modules and limit the interaction between them.&lt;/p&gt;
&lt;h3&gt;Metaprogramming&lt;/h3&gt;
&lt;p&gt;The systems that we are trying to build should be highly configurable. Use metadata to describe configuration options for an application. Switching communication port should be better done via configuration than getting into the coding details. We should appreciate the values of metaprogramming.&lt;/p&gt;
&lt;h3&gt;Temporal coupling&lt;/h3&gt;
&lt;p&gt;There are various things in our day to day life which can be made concurrent. So are in software. Many of the tasks don’t depend on each other, so executing them concurrently can give a great deal of performance improvement. Temporal coupling is about analyzing workflow to find and improve concurrency.&lt;/p&gt;
&lt;h2&gt;While you are coding&lt;/h2&gt;
&lt;p&gt;Some advice on how you should code and how you should not!&lt;/p&gt;
&lt;h3&gt;Program by Coincidence&lt;/h3&gt;
&lt;p&gt;Ever faced the situation when you don’t know why the code is failing because you didn’t know why it worked in the first place. It seemed to work, given the limited “testing” that you did, but that was just a coincidence. I had many of these moments. So code deliberately. Proceed with a plan, always be aware of what you are doing and document your assumptions.&lt;/p&gt;
&lt;h3&gt;Estimating algorithm speed&lt;/h3&gt;
&lt;p&gt;Estimate the runtime of your algorithm. A Big-O analysis of your codebase would help you identify major areas of improvement in terms of computation.&lt;/p&gt;
&lt;h3&gt;Refactoring&lt;/h3&gt;
&lt;p&gt;Refactoring is a change made to the internal structure of software to make it easier to understand and cheaper to modify without changing its observable behaviour. We refactor to make sure our program is not rotting. You should refactor when you see a violation of the DRY principle. When you see nonorthogonality try to refactor to the best possible extent. Remove outdated knowledge from the codebase. Refactor early, refactor often.&lt;/p&gt;
&lt;h3&gt;Testing&lt;/h3&gt;
&lt;p&gt;A lot has been said and discussed on testing. You should write proper unit tests. Testing is more cultural than technical. It’s language and framework independent. Create a culture of testing.&lt;/p&gt;
&lt;h2&gt;Before the project&lt;/h2&gt;
&lt;p&gt;Most of the times when a project starts, requirements are not locked. Requirements are never fixed. They keep evolving as the customer and the developer get closer to understanding what they want to consume/build. Most of the times we need to dig for requirements by having one on one sessions with the consumer to identify the expectations rigidly.
Promote abstractions. Talk in terms of abstractions because abstractions live longer than the implementations.&lt;/p&gt;
&lt;p&gt;So that was a brief summary of what makes a pragmatic programmer. I have started to adopt these pieces of advice as I care more of my craft now than ever.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Thanks for reading !!! Feel free to connect with me on &lt;a href=&quot;https://twitter.com/whoAbhishekSah&quot;&gt;Twitter&lt;/a&gt; for any conversations on this.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Freya - The fondness for lambda architecture]]></title><description><![CDATA[Building a product that ingests and processes huge volumes of data online — and why we reached for lambda architecture.]]></description><link>https://absh.dev/Freya - the-fondness-for-lambda-architecture/</link><guid isPermaLink="false">https://absh.dev/Freya - the-fondness-for-lambda-architecture/</guid><pubDate>Sat, 30 Mar 2019 22:12:03 GMT</pubDate><content:encoded>&lt;p&gt;How we went on building a product which can handle a huge amount of incoming data and process it online.&lt;/p&gt;
&lt;p&gt;Hi Everyone,&lt;/p&gt;
&lt;p&gt;I am here to talk about one of the projects that I did at my college. I took up this project during the final year of engineering at IIT J. This project is actually very different compared to other projects that I had done until then(which includes web development, NLP, Machine Learning etc.). We were a team of two. We had our fair share of difficulties and hurdles on the way. But that’s where the learnings come from 😃.&lt;/p&gt;
&lt;h3&gt;Problem Statement:&lt;/h3&gt;
&lt;p&gt;Handling inflow of huge amount of data and processing it online.&lt;/p&gt;
&lt;p&gt;Let’s try to visualise the problem statement with a use case.&lt;/p&gt;
&lt;p&gt;Consider an academic institution such as mine. When some student wants to perform data processing for her project on some huge amount of data, it is very tough to do it on her single computer/laptop due to the resource limit. We are talking data of terabytes and petabytes scale. So doing the processing on a single computer will take forever. We want to develop a solution for this pro­blem. The commodity PCs at the computer laboratory can be used to distribute the task in hand. We want to develop a system so generic which will perform the desired job on the huge incoming data by processing it online. We also want it to be fault tolerant at all times. There are plenty of tools that do distribute computing but rarely there are such systems for this context.&lt;/p&gt;
&lt;p&gt;So let’s say we have an incoming stream of images and the developer wants to run some classification algorithm over those images. The result of this classification should be written in a database for online queries. Running the desired job on each piece of data(here classification of images) linearly will take forever.&lt;/p&gt;
&lt;p&gt;Can we distribute the classification task to more computers and aggregate the result when the job is done?&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://lambda-architecture.net/&quot;&gt;Lambda Architecture(LA)&lt;/a&gt; is one such solution. Nathan Marz described lambda architecture as a robust, scalable, generic, distributed system. Let’s talk about how LA handles this. Please bear with me. It will be worth it 😇.&lt;/p&gt;
&lt;h3&gt;What is Lambda Architecture&lt;/h3&gt;
&lt;p&gt;It all begins with data. Data of any type e.g. logs from a mobile application, location pings, images, audio, videos, banking transaction. The raw data is stored in the data centres. On top of that various operations are performed. LA makes two assumptions on the data. The assumption on this data is that it is online and it is immutable. The former online, here means that we are appending the new data at the end of existing data. The latter term immutable, means we are not changing any data at any point in time. Updates are replaced by new writes. I’ll justify these assumptions in a while.&lt;/p&gt;
&lt;p&gt;Now you have queries on the data (the whole dataset). Literally running a function on the complete dataset has latency constraints as dataset size grows. For the time being let’s assume we are able to compute our function quickly on complete dataset. Talking about CAP theorem, partition tolerance is a necessity and not an option. So the tradeoff is between consistency and availability. Nathan Marz, proves in his work, that how mutable data and CAP theorem don’t play well together (the complexity of read-repairs). However, if we remove the updates by new writes there won’t be any divergent values at any point in time. So we will be able to achieve eventual consistency along with availability. CAP theorem has been beaten.&lt;/p&gt;
&lt;p&gt;Talking about eventual consistency, we assumed that our function runs quickly on the entire dataset. This can be achieved by using a data system which can easily store large and constantly growing dataset and can compute the function in a scalable way.&lt;/p&gt;
&lt;p&gt;To get results quicker, we can precompute the batch views and store in a database. This database can be indexed on the timestamp for quicker queries.&lt;/p&gt;
&lt;p&gt;The only drawback that can arise here as the data grows is that the queries are out of date by a few hours. There will be eventual consistency. All that is left to do is to compensate for the last few hours of data. Nathan proposes a real-time system that computes the query on the last few hours of data for which batch view has not been calculated. This can be done quickly. So to finally answer a query on the whole dataset, we can merge the results from batch view and real-time view. The last few hours of data are again sent to batch layer for batch view precomputation. Let’s understand this approach with an example:&lt;/p&gt;
&lt;p&gt;Let’s say you have 100 GB of images so far and the data is ever increasing. You compute you classification job on this whole dataset in let’s say ~5 hrs. You have written the result of this computation in a dataset indexed with the timestamp. Now say 10 MB of new images have arrived. Recomputing the classification job on 100GB+10MB will again take ~5 hrs. This will be really an inefficient way of handling the data and queries since you’ve precomputed the result on 100GB earlier.&lt;/p&gt;
&lt;h3&gt;How does Lambda Architecture work?&lt;/h3&gt;
&lt;p&gt;We can process the existing data in a batch processing framework(e.g Apache Hadoop), writing the result of the classification job in a database. So we have precomputed the batch view. The new incoming data can be processed inside a real-time processing framework(e.g. Apache Storm) and the result could again be written in a separate database. When a user queries about the job on the whole dataset, we can show results of the precomputed batch views aggregated with real-time views quickly. The new data can be sent again to the batch layer to be appended and job to be recomputed in the background on the new batch view. That way we have distributed the job among many computers and answering queries quickly.&lt;/p&gt;
&lt;h3&gt;Building Blocks of Freya&lt;/h3&gt;
&lt;p&gt;Freya extends this same idea in our context. You give it some computers with Ubuntu installed on them connected in a network. It will set up the batch layer and real-time layer along with a database gateway to query on the data. We used Apache Hadoop for the batch layer and Apache Storm for the real-time layer.&lt;/p&gt;
&lt;p&gt;The batch layer of Freya is a master-slave Hadoop architecture. Master node runs the NameNode and the slave nodes act as DataNodes. Here is a diagram describing this architecture:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/31c8e6d965c11e632ba9b164be204fd8/c84e8/1.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 53.37837837837838%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAEDBf/EABUBAQEAAAAAAAAAAAAAAAAAAAEA/9oADAMBAAIQAxAAAAHcVFExif/EABkQAQACAwAAAAAAAAAAAAAAAAEAEQIQMf/aAAgBAQABBQK4K4pp5Qn/xAAVEQEBAAAAAAAAAAAAAAAAAAAAEf/aAAgBAwEBPwFH/8QAFREBAQAAAAAAAAAAAAAAAAAAABH/2gAIAQIBAT8BV//EABoQAAICAwAAAAAAAAAAAAAAAAABEBEhIjL/2gAIAQEABj8CZsqObnJ//8QAGxABAAIDAQEAAAAAAAAAAAAAAQARMUFREGH/2gAIAQEAAT8hHgK6IhbXLgO31fmaBTlXZ//aAAwDAQACAAMAAAAQb/8A/8QAFxEAAwEAAAAAAAAAAAAAAAAAAAHwEf/aAAgBAwEBPxDByR//xAAVEQEBAAAAAAAAAAAAAAAAAAAAAf/aAAgBAgEBPxBD/8QAHhABAAIBBAMAAAAAAAAAAAAAAQARIRAxQYGRobH/2gAIAQEAAT8QSIQWpV9xMOa6n3EB2XAfXQeNhjKhm0//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;The batch layer of Freya&quot;
        title=&quot;&quot;
        src=&quot;/static/31c8e6d965c11e632ba9b164be204fd8/1c72d/1.jpg&quot;
        srcset=&quot;/static/31c8e6d965c11e632ba9b164be204fd8/a80bd/1.jpg 148w,
/static/31c8e6d965c11e632ba9b164be204fd8/1c91a/1.jpg 295w,
/static/31c8e6d965c11e632ba9b164be204fd8/1c72d/1.jpg 590w,
/static/31c8e6d965c11e632ba9b164be204fd8/c84e8/1.jpg 743w&quot;
        sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The real-time layer of Freya is also master-slave Storm architecture. Master node runs the Zookeeper server, Nimbus, UI and supervisor and the slave nodes act as supervisor nodes. Here is a diagram describing this architecture:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9b6ac2c8637e00b6c9cd6f71c75d6b33/3fd11/2.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 51.35135135135135%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAKABQDASIAAhEBAxEB/8QAGAAAAwEBAAAAAAAAAAAAAAAAAAECAwX/xAAVAQEBAAAAAAAAAAAAAAAAAAAAAf/aAAwDAQACEAMQAAAB7s6JJGH/xAAZEAEAAgMAAAAAAAAAAAAAAAABETEAEBL/2gAIAQEAAQUClwnnTRX/xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAEDAQE/AT//xAAUEQEAAAAAAAAAAAAAAAAAAAAQ/9oACAECAQE/AT//xAAZEAACAwEAAAAAAAAAAAAAAAAAMQEgISL/2gAIAQEABj8CR1Gip//EAB0QAQABAwUAAAAAAAAAAAAAAAEAESExEFFhcZH/2gAIAQEAAT8hyeKxO0Ngy3LvTPAJqT//2gAMAwEAAgADAAAAEC8f/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAHBABAAMAAgMAAAAAAAAAAAAAAQARIRAxQZGh/9oACAEBAAE/EBMqwxgYifOrX2VQtY7RfB9MYQWjUn//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;The real-time layer of Freya&quot;
        title=&quot;&quot;
        src=&quot;/static/9b6ac2c8637e00b6c9cd6f71c75d6b33/1c72d/2.jpg&quot;
        srcset=&quot;/static/9b6ac2c8637e00b6c9cd6f71c75d6b33/a80bd/2.jpg 148w,
/static/9b6ac2c8637e00b6c9cd6f71c75d6b33/1c91a/2.jpg 295w,
/static/9b6ac2c8637e00b6c9cd6f71c75d6b33/1c72d/2.jpg 590w,
/static/9b6ac2c8637e00b6c9cd6f71c75d6b33/3fd11/2.jpg 775w&quot;
        sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The database gateway is a Node.js app which exposes various API endpoints to query the databases for the result of the job performed on the whole dataset.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/8ab792a186d108f4bc0290e6b6b1103e/b040c/3.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 34.45945945945946%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAAHABQDASIAAhEBAxEB/8QAFwABAAMAAAAAAAAAAAAAAAAAAAECBf/EABUBAQEAAAAAAAAAAAAAAAAAAAAB/9oADAMBAAIQAxAAAAHZkSoX/8QAGBABAQEBAQAAAAAAAAAAAAAAAgESAxH/2gAIAQEAAQUCPlvTWQqx/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPwE//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPwE//8QAGhAAAQUBAAAAAAAAAAAAAAAAAAECESIxYf/aAAgBAQAGPwLpTRHZJ//EABkQAAMBAQEAAAAAAAAAAAAAAAABEVEhcf/aAAgBAQABPyF/D6OtxGistYQ//9oADAMBAAIAAwAAABD8D//EABURAQEAAAAAAAAAAAAAAAAAAAEQ/9oACAEDAQE/ECf/xAAWEQEBAQAAAAAAAAAAAAAAAAABEBH/2gAIAQIBAT8QTJ//xAAZEAEAAwEBAAAAAAAAAAAAAAABABFBITH/2gAIAQEAAT8QUJ0YxGSQU0bC8Iv2rJ//2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Database Gateway of Freya&quot;
        title=&quot;&quot;
        src=&quot;/static/8ab792a186d108f4bc0290e6b6b1103e/1c72d/3.jpg&quot;
        srcset=&quot;/static/8ab792a186d108f4bc0290e6b6b1103e/a80bd/3.jpg 148w,
/static/8ab792a186d108f4bc0290e6b6b1103e/1c91a/3.jpg 295w,
/static/8ab792a186d108f4bc0290e6b6b1103e/1c72d/3.jpg 590w,
/static/8ab792a186d108f4bc0290e6b6b1103e/b040c/3.jpg 844w&quot;
        sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;So overall we have an aggregation of these different components as the final product. Of course, it is not the best solution as it has its own limitations. For example, we have increased the number of point of failures. But given the optimization in terms of time that we are getting here is remarkable.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; &quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/b03ddf1b642f6d1440370213464496c8/dde82/4.jpg&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 57.432432432432435%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/jpeg;base64,/9j/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdASFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wgARCAALABQDASIAAhEBAxEB/8QAFwAAAwEAAAAAAAAAAAAAAAAAAAECBf/EABQBAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhADEAAAAd1uRlB//8QAGBAAAwEBAAAAAAAAAAAAAAAAAAEhEEH/2gAIAQEAAQUCVRc6f//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQMBAT8BP//EABQRAQAAAAAAAAAAAAAAAAAAABD/2gAIAQIBAT8BP//EABUQAQEAAAAAAAAAAAAAAAAAACEg/9oACAEBAAY/Akr/xAAaEAADAAMBAAAAAAAAAAAAAAAAAREQMVGh/9oACAEBAAE/IaS1rglNF49JRKQtTH//2gAMAwEAAgADAAAAEDAP/8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAwEBPxA//8QAFBEBAAAAAAAAAAAAAAAAAAAAEP/aAAgBAgEBPxA//8QAGxABAAICAwAAAAAAAAAAAAAAAQARMUEQUaH/2gAIAQEAAT8QfehDiEqSgFb9iGrCMi6gUAAUGuP/2Q==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Complete architecture of Freya&quot;
        title=&quot;&quot;
        src=&quot;/static/b03ddf1b642f6d1440370213464496c8/1c72d/4.jpg&quot;
        srcset=&quot;/static/b03ddf1b642f6d1440370213464496c8/a80bd/4.jpg 148w,
/static/b03ddf1b642f6d1440370213464496c8/1c91a/4.jpg 295w,
/static/b03ddf1b642f6d1440370213464496c8/1c72d/4.jpg 590w,
/static/b03ddf1b642f6d1440370213464496c8/a8a14/4.jpg 885w,
/static/b03ddf1b642f6d1440370213464496c8/dde82/4.jpg 910w&quot;
        sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3&gt;Merits and shortcomings of Freya&lt;/h3&gt;
&lt;p&gt;Some good things about Freya:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Since the input data is never modified, it makes huge map reduce jobs tractable. The jobs are easy to control and each stage in the job can be debugged easily.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The code keeps changing. The jobs keep changing. It may be because of bugs or requirement changes or other reasons. But Freya(since it is based on top of LA) is resilient to this change. Batch processing deals with this problem easily by re-computing.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are some shortcomings as well.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The duplicative development effort in building hot(real-time) and cold(offline) paths of their processing pipeline. The additional overhead of reprocessing the data in the batch layer which were processed in the stream layer. The overhead of merging the results of both layer also counts as developer overhead.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Maintaining the code that produces the same result in every complex distributed framework. There are many points of failures.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Operational overhead is high for maintaining, running and debugging two systems simultaneously.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So why use Freya depends on the use case. In our case, lambda architecture is best-fit considering the academic use cases. So operational overhead is considerably low. Also, the nodes are physically located in one place. So it’s easy to debug and the infrastructure is less network error prone.&lt;/p&gt;
&lt;p&gt;We have tried to achieve automation to the greatest extent possible. With Freya, your in-house LA will be up and running within minutes ✌️. Below is a demo.&lt;/p&gt;
&lt;div class=&quot;gatsby-resp-iframe-wrapper&quot; style=&quot;padding-bottom: 56.25%; position: relative; height: 0; overflow: hidden; margin-bottom: 1.0725rem&quot; &gt; &lt;iframe src=&quot;https://www.youtube.com/embed/ccbEO9abQpw&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot; style=&quot; position: absolute; top: 0; left: 0; width: 100%; height: 100%; &quot;&gt;&lt;/iframe&gt; &lt;/div&gt;
&lt;h3&gt;Setting up Freya&lt;/h3&gt;
&lt;p&gt;The code for Freya is on &lt;a href=&quot;https://github.com/rashi-sahu/Freya&quot;&gt;Github&lt;/a&gt;. This project would not have been successful without the contributions of my project partner &lt;a href=&quot;https://github.com/rashi-sahu&quot;&gt;Rashi&lt;/a&gt; and guidance of our mentor &lt;a href=&quot;https://www.linkedin.com/in/sumit-kalra-97a06933/&quot;&gt;Dr Sumit Kalra&lt;/a&gt;. I am thankful to them for their inputs. We are thinking of adding more features and bringing it to the usage of the common.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Thanks for reading !!! Feel free to connect with me on &lt;a href=&quot;https://twitter.com/whoAbhishekSah&quot;&gt;Twitter&lt;/a&gt; for any conversations.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[JSON vs Protocol Buffer]]></title><description><![CDATA[Comparing JSON and Protocol Buffers for service communication — size, speed, schema evolution, and when each makes sense.]]></description><link>https://absh.dev/JSON-vs-Protocol-buffer/</link><guid isPermaLink="false">https://absh.dev/JSON-vs-Protocol-buffer/</guid><pubDate>Sat, 09 Jun 2018 22:12:03 GMT</pubDate><content:encoded>&lt;h4&gt;Introduction:&lt;/h4&gt;
&lt;p&gt;Data is everywhere. Computer programs perform different operations on the data to make meaning out of them. There may be a need of data exchange among programs as well. For ex. say a mobile app(client) may ask data from an API endpoint(server) or a web server may query a database for data etc. This article is about the data exchange part.&lt;/p&gt;
&lt;p&gt;So how can a program written in a language (say Ruby) exchange data with another program written in language (say JavaScript) over a network. We need some uniform format for data which all languages are compatible with, which all language can easily understand and parse efficiently in their data structures. We first encode the raw data in that format and serialize it, send it over the network or make it available to other programs over a network.&lt;/p&gt;
&lt;h3&gt;JSON&lt;/h3&gt;
&lt;p&gt;JSON is one such format. JSON stands for Javascript Object Notation. It’s a lightweight data interchange format. A JSON object is typically used to contain some data in key/value format. It looks like a string wrapped in curly braces with colons between the names and values, and commas between the values and names. See example below.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Abhishek&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Read a book&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;time&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2200&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here &lt;em&gt;id&lt;/em&gt;, &lt;em&gt;name&lt;/em&gt;, &lt;em&gt;message&lt;/em&gt; and &lt;em&gt;time&lt;/em&gt; are the keys with values &lt;em&gt;2&lt;/em&gt;, &lt;em&gt;Abhishek&lt;/em&gt;, &lt;em&gt;Read a book&lt;/em&gt; and &lt;em&gt;2200&lt;/em&gt; respectively. It is analogous to hash(map). The order in which these keys appear don’t matter.&lt;/p&gt;
&lt;p&gt;A JSON Array is a collection of JSON objects wrapped in &lt;code class=&quot;language-text&quot;&gt;[ ]&lt;/code&gt; separated by a comma. It is analogous to array. The order matters here. See example below.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Abhishek&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Read a book&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;time&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2200&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;3&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Uneet&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Order Lunch&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;time&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1400&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The JSON object and JSON arrays can be nested in each other as well. See example below.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;room&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;G75&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;data&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Abhishek&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;3&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Uneet&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;JSON supports few basic data types.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Numbers&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;String&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Boolean&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Array(Ordered list)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Object (Unordered list)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now we know how we can represent our data in JSON. We can encode our data in our program in JSON format. Some other program may ask for this data and decode it. Mostly all programming languages now-a-days have methods in their standard libraries which perform encoding and decoding of the data with just one line of code.&lt;/p&gt;
&lt;h3&gt;Protocol Buffer&lt;/h3&gt;
&lt;p&gt;Protocol Buffer is another such data-interchange format invented by Google. It is smaller, faster, and simpler than JSON and other data formats out there. JSON is a simple method to represent data. Protocol buffer introduces a schema in the data. For this we first define a (.proto) file. It contains how we want to structure our data. The first JSON object example that I stated earlier, can be given a structure in protocol buffer as given below.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;java&quot;&gt;&lt;pre class=&quot;language-java&quot;&gt;&lt;code class=&quot;language-java&quot;&gt;message &lt;span class=&quot;token class-name&quot;&gt;Todo&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
optional int32 id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
 required string name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
 required string message &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
 required &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;google&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;protobuf&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/span&gt;Timestamp&lt;/span&gt; time &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Note that how we can specify data types (int32, int64, string, timestamp etc), the data labels (optional, required) in our .proto file. Each field has a unique tag. The 1, 2, 3 and 4 in the RHS are the tags to fields. The numbered tags are used to match fields when serializing and deserializing the data.&lt;/p&gt;
&lt;p&gt;This gives much more flexibility to extract meaning out of data in an easy manner as compared to JSON. This is a pretty simple example of a proto file. Protocol Buffer have support for namespaces, enumerations as well. After defining the structure, you run the protocol buffer compiler for your application’s language on your (.proto) file to generate data access classes. They will give you simple accessor function for each field.&lt;/p&gt;
&lt;p&gt;JSON specifies the key(field) in every object which increases the data size. Protocol buffer uses the tags to identify fields. So it’s memory efficient than JSON.&lt;/p&gt;
&lt;p&gt;A schema is very useful in maintaining the structure of data. It counters many inconsistencies that may get introduced if structure was not defined explicitly.&lt;/p&gt;
&lt;p&gt;Protocol Buffers have several more advantages over JSON. You can add new fields without breaking anything. Old binaries simply ignore the new field when parsing. This is called backward compatibility.&lt;/p&gt;
&lt;p&gt;I highly recommend you to see the code samples on the &lt;a href=&quot;https://developers.google.com/protocol-buffers/&quot;&gt;official documentation&lt;/a&gt; of Protocol Buffers. This makes concepts really clear via code samples and working examples in many languages.&lt;/p&gt;</content:encoded></item></channel></rss>