<?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[Go & DevOps Blog]]></title><description><![CDATA[English blog about Go programming, DevOps, CI/CD, Docker, Kubernetes, and practical engineering tips from a developer with 5 years of experience.]]></description><link>https://blog.fshtab.com</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1766393796435/e2202ffe-975f-4054-ab51-d21ba4d88ccb.png</url><title>Go &amp; DevOps Blog</title><link>https://blog.fshtab.com</link></image><generator>RSS for Node</generator><lastBuildDate>Sat, 11 Apr 2026 09:18:43 GMT</lastBuildDate><atom:link href="https://blog.fshtab.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[The most popular Go dependency is…]]></title><description><![CDATA[testify!
As you know, destination is not as important as the journey, so now that we got this out of the way, bear with me for the rest of this article, and I’ll give you the top 10, and many more stats 😊 You might even learn a few things along the ...]]></description><link>https://blog.fshtab.com/the-most-popular-go-dependency-is</link><guid isPermaLink="true">https://blog.fshtab.com/the-most-popular-go-dependency-is</guid><category><![CDATA[golang]]></category><category><![CDATA[Open Source]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[Databases]]></category><category><![CDATA[Neo4j]]></category><category><![CDATA[testify]]></category><dc:creator><![CDATA[Fedor S]]></dc:creator><pubDate>Sat, 24 Jan 2026 08:47:23 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1769244293010/530eb83e-2c06-4ab1-a704-b13e3cf0c737.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><a target="_blank" href="https://github.com/stretchr/testify"><strong>testify</strong></a>!</p>
<p>As you know, destination is not as important as the journey, so now that we got this out of the way, bear with me for the rest of this article, and I’ll give you the top 10, and many more stats 😊 You might even learn a few things along the way!</p>
<hr />
<p>Without usage statistics, finding useful and reliable dependencies can be a bit of a challenge. In the Go community, we basically have to rely either:</p>
<ul>
<li><p>on "brand" name reputation: there are some very well known packages (<a target="_blank" href="https://github.com/gin-gonic/gin"><strong>gin</strong></a>, <a target="_blank" href="https://github.com/spf13/cobra"><strong>cobra</strong></a>, <a target="_blank" href="https://github.com/stretchr/testify"><strong>testify</strong></a>…) and organizations (<a target="_blank" href="https://github.com/google?q=&amp;type=all&amp;language=go&amp;sort=stargazers"><strong>Google</strong></a>, <a target="_blank" href="https://github.com/gorilla"><strong>gorilla</strong></a>…) that we can trust,</p>
</li>
<li><p>or on side metrics such as number of GitHub stars, number of open issues, last activity, and more.</p>
</li>
</ul>
<p>All of this can sometimes help get a feeling of how widely used and trusted a Go module is, but we could get more. What I personally want is knowing how many times a module is actually required as a project dependency to get a feeling of how "battle-tested" a library is. But to do this, I would need to build a graph of the whole (open source) ecosystem, which would be insane… You can see where this is going 😉</p>
<h2 id="heading-mapping-the-go-ecosystem"><strong>Mapping the Go ecosystem</strong></h2>
<p>My <strong>first idea</strong> was to build a list of repositories (a seed) to use as a starting point. The goal was to read the dependencies of these modules from their <code>go.mod</code>, download each of them, read the dependencies of these modules from their <code>go.mod</code>, download each of them, read the dependencies of these modules from their <code>go.mod</code>, download each of… well, you know the deal.</p>
<p>I implemented this idea on the <a target="_blank" href="https://github.com/Thiht/go-stats/tree/v1"><code>v1</code></a> branch of my repository using mainly <a target="_blank" href="https://evanli.github.io/Github-Ranking/"><strong>Github-Ranking</strong></a> and <a target="_blank" href="https://awesome-go.com/"><strong>awesome-go</strong></a> as sources for building the seed. I ultimately abandoned the idea because of a few shortcomings:</p>
<ul>
<li><p>the sample is largely incomplete,</p>
</li>
<li><p>cloning so many Git repositories to find their <code>go.mod</code> is reaaally painful and slow,</p>
</li>
<li><p>and it’s particularly biased towards repositories hosted on GitHub.</p>
</li>
</ul>
<hr />
<p>Luckily for me, I came up with a <strong>second idea</strong>: the Go modules ecosystem relies on a centralized public proxy, so surely they expose some information on these modules. And they in fact do so! The proxy APIs are documented on <a target="_blank" href="https://proxy.golang.org/"><strong>proxy.golang.org</strong></a>:</p>
<ul>
<li><p><a target="_blank" href="https://go.dev/ref/mod#goproxy-protocol"><strong>proxy.golang.org</strong></a> exposes metadata on each module (versions, latest, mod file…),</p>
</li>
<li><p><a target="_blank" href="http://index.golang.org/"><strong>index.golang.org</strong></a> exposes a feed of all the published module versions since the introduction of the Go proxy (<code>2019-04-10T19:08:52.997264Z</code>, if you want to make sure not to forget its birthday).</p>
</li>
</ul>
<p>I used this information to locally download the whole index (module names and versions) since 2019. The downloaded data is available in <a target="_blank" href="https://github.com/Thiht/go-stats/tree/main/data/goproxy-modules"><strong>goproxy-modules</strong></a>. This can be used as a local immutable cache.</p>
<aside>For the full implementation details, see:<p></p><ul><li><a href="https://github.com/Thiht/go-stats/blob/main/cmd/list-goproxy-modules.go"><code>list-goproxy-modules.go</code></a></li><li><a href="https://github.com/Thiht/go-stats/blob/main/goproxy/goproxy.go"><code>goproxy.go</code></a></li></ul></aside>

<p>With all this data available locally, the seed is now pretty much exhaustive, and more suitable for data analysis. The processing now simply consists of iterating over every single module, downloading their <code>go.mod</code> file and listing their dependencies. The resulting graph can then trivially be inserted in a specialized graph database like Neo4j.</p>
<h2 id="heading-deep-diving"><strong>Deep diving</strong></h2>
<p><img src="https://blog.thibaut-rousseau.com/blog/the-most-popular-go-dependency-is/neo4j.png" alt="Neo4j logo" /></p>
<p>Neo4j is a graph oriented database. It means that unlike relational databases, it works on... graphs. The primary way to store data in Neo4j is using nodes and relationships. This specialized data structure makes it extremely simple to model, and more importantly query huge graphs.</p>
<aside>If you want to experiment with Neo4j, I would recommend using the go-stats<span> </span><a href="https://github.com/Thiht/go-stats/blob/main/docker-compose.yml"><code>docker-compose.yml</code></a><span> </span>file. You can then open<span> </span><a href="http://localhost:7474/browser/">localhost:7474</a><span> </span>(no credentials needed) to use the Neo4j browser.</aside>

<p>Neo4j, like many NoSQL databases, is schemaless, meaning you don't need to define a schema before creating data. That doesn't mean we don't need a schema, so let's see what we need!</p>
<pre><code class="lang-plaintext">DEPENDS_ONModulestringnamestringversion
</code></pre>
<p>A Go module is basically identified by its name (eg. <code>github.com/stretchr/testify</code> or <code>go.yaml.in/yaml/v4</code>) and its version. Each module can depend on other modules. We can add more properties to our nodes later on as needed.</p>
<h3 id="heading-creating-nodes"><strong>Creating nodes</strong></h3>
<p>Neo4j uses <a target="_blank" href="https://neo4j.com/docs/cypher-manual/current/introduction/"><strong>Cypher</strong></a> as a query language. Inserting data with Cypher can be done with the <a target="_blank" href="https://neo4j.com/docs/cypher-manual/current/clauses/create/"><code>CREATE</code></a> clause, but in go-stats I've decided to use the <a target="_blank" href="https://neo4j.com/docs/cypher-manual/current/clauses/merge/"><code>MERGE</code></a> clause instead because it behaves as an upsert, letting you update or do nothing in case a node already exists.</p>
<p>The basic Cypher query to upsert a module node is:</p>
<pre><code class="lang-plaintext">MERGE (m:Module { name: $name, version: $version })
RETURN m
</code></pre>
<p><code>:Module</code> is a label attached to the node. You can think of it as the type of the node. <code>name</code> and <code>version</code> are properties of the node, they're the data belonging to each specific node.</p>
<p>To make sure we can't create multiple nodes with the same name-version pair, a unicity constraint is needed:</p>
<pre><code class="lang-plaintext">CREATE CONSTRAINT module_identity IF NOT EXISTS
FOR (m:Module)
REQUIRE (m.name, m.version) IS UNIQUE
</code></pre>
<p>Thanks to this constraint, if <code>MERGE</code> is called a second time with the same <code>name</code> and <code>version</code> properties, it won't do anything.</p>
<h3 id="heading-creating-relationships"><strong>Creating relationships</strong></h3>
<p>We can then create the dependency relationships between our module nodes:</p>
<pre><code class="lang-plaintext">MATCH (dependency:Module { name: $dependencyName, version: $dependencyVersion })
MATCH (dependent:Module { name: $dependentName, version: $dependentVersion })
MERGE (dependent)-[:DEPENDS_ON]-&gt;(dependency)
RETURN dependency, dependent
</code></pre>
<p>This query will:</p>
<ol>
<li><p>find modules that were created earlier using the <a target="_blank" href="https://neo4j.com/docs/cypher-manual/current/clauses/match/"><code>MATCH</code></a> clause and assign them to <code>dependency</code> and <code>dependent</code>,</p>
</li>
<li><p>create the directed relationship between them using the <code>-[:DEPENDS_ON]-&gt;</code> syntax.</p>
</li>
</ol>
<p>The Go index is naturally sorted chronologically. So as long as we iterate over it sequentially, it means that if a module (<em>dependent</em>) depends on another module (<em>dependency</em>), then <em>dependency</em> was necessarily added to the graph before <em>dependent</em>. If this condition doesn't hold for some reason (if we were to decide to parallelize the insertions for example), we could simply rewrite the query as:</p>
<pre><code class="lang-plaintext">MERGE (dependency:Module { name: $dependencyName, version: $dependencyVersion })
MERGE (dependent:Module { name: $dependentName, version: $dependentVersion })
MERGE (dependent)-[:DEPENDS_ON]-&gt;(dependency)
RETURN dependency, dependent
</code></pre>
<p>Using <code>MERGE</code> instead of <code>MATCH</code> would ensure the node gets created if it doesn't exist already.</p>
<hr />
<p>These queries are simplified (but close!) variants of what I actually did in go-stats. The main difference is that I enriched the nodes with some additional properties:</p>
<ul>
<li><p>version timestamp,</p>
</li>
<li><p>latest version,</p>
</li>
<li><p>semantic version splitting (major, minor, patch, label),</p>
</li>
<li><p>host, organisation, and more.</p>
</li>
</ul>
<aside>For the full implementation details, see:<span> </span><a href="https://github.com/Thiht/go-stats/blob/main/cmd/process-modules.go"><code>process-modules.go</code></a></aside>

<h2 id="heading-digging-into-the-graph"><strong>Digging into the graph</strong></h2>
<p>After running go-stats for a few days, I ended up with a graph of roughly <strong>40 million nodes</strong>, and <strong>400 million relationships</strong>… that's quite a lot! The first thing these numbers tell us is that Go modules have <strong>10 direct dependencies on average</strong>.</p>
<p><img src="https://blog.thibaut-rousseau.com/blog/the-most-popular-go-dependency-is/data-analyst.jpg" alt="Meme showing a dog in a scientist outfit saying: I'm a data analyst now" /></p>
<p>For more interesting stats, let's write some Cypher, shall we?</p>
<h3 id="heading-indexing"><strong>Indexing</strong></h3>
<p>With this volume of data, the absolute first thing to do (that I clearly didn't do at first) is to create relevant indexes. I was initially under the impression that the <code>module_identity</code> constraint previously created would also act as an index for <code>:Module.name</code> since it's a composite unique index. I was wrong, and creating a specific index was necessary:</p>
<pre><code class="lang-plaintext">CREATE INDEX module_name_idx IF NOT EXISTS
FOR (m:Module) ON (m.name)
</code></pre>
<p>I created other indexes as needed, and to do so the Cypher <a target="_blank" href="https://neo4j.com/docs/cypher-manual/current/planning-and-tuning/"><code>PROFILE</code></a> was a tremendous help.</p>
<h3 id="heading-find-the-direct-dependents-of-a-module"><strong>Find the direct dependents of a module</strong></h3>
<p>As a warm-up, and to learn a bit more about Cypher, let's list the dependents of a specific module:</p>
<pre><code class="lang-plaintext">MATCH (dependency:Module { name: 'github.com/pkg/errors', version: 'v0.9.1' })
MATCH (dependent:Module)-[:DEPENDS_ON]-&gt;(dependency)
WHERE dependent.isLatest
RETURN dependent.versionTime.year AS year, COUNT(dependent) AS nbDependents
</code></pre>
<p>I chose <a target="_blank" href="https://github.com/pkg/errors/tree/v0.9.1"><code>github.com/pkg/errors@v0.9.1</code></a> because it's a module that was deprecated long ago, I find it interesting to know how much it's still used in the wild. Let's break the query down line by line:</p>
<ol>
<li><p><code>MATCH (dependency:Module { name: 'xxx', version: 'xxx' })</code></p>
<ul>
<li><p>finds the module node (we know it's unique because of the constraint we declared earlier) with the given <code>name</code> and <code>version</code>. This is equivalent to:</p>
<pre><code class="lang-plaintext">  MATCH (dependency:Module)
  WHERE dependency.name = 'xxx'
  AND dependency.version = 'xxx'
</code></pre>
</li>
</ul>
</li>
<li><p><code>MATCH (dependency)&lt;-[:DEPENDS_ON]-(dependent:Module)</code></p>
<ul>
<li>finds the module nodes with a direct <code>DEPENDS_ON</code> relationship towards <code>dependency</code>.</li>
</ul>
</li>
<li><p><code>WHERE dependent.isLatest</code></p>
<ul>
<li>keeps <code>dependents</code> modules that are in their latest version. This is useful because for any module <code>x</code> depends on <code>github.com/pkg/errors@v0.9.1</code>, we don't want to count all the versions of <code>x</code> that depend on it. The latest is more relevant to us.</li>
</ul>
</li>
<li><p><code>RETURN dependent.versionTime.year AS year, COUNT(dependent) AS nbDependents</code></p>
<ul>
<li><p>simply counts the total dependents of <code>github.com/pkg/errors@v0.9.1</code> and group by release year of the dependent. If we wanted to list them, we could write:</p>
<pre><code class="lang-plaintext">  RETURN dependent.name AS dependentName
  ORDER BY dependentName;
</code></pre>
</li>
</ul>
</li>
</ol>
<p><strong>Results:</strong></p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>year</strong></td><td><strong>nbDependents</strong></td></tr>
</thead>
<tbody>
<tr>
<td>2019</td><td>3</td></tr>
<tr>
<td>2020</td><td>6,774</td></tr>
<tr>
<td>2021</td><td>10,680</td></tr>
<tr>
<td>2022</td><td>11,747</td></tr>
<tr>
<td>2023</td><td>8,992</td></tr>
<tr>
<td>2024</td><td>12,220</td></tr>
<tr>
<td>2025</td><td>16,001</td></tr>
</tbody>
</table>
</div><p>That's a lot of dependents for a dead library!</p>
<h3 id="heading-find-the-transitive-dependents-of-a-module"><strong>Find the transitive dependents of a module</strong></h3>
<p>Neo4j really shines at graph traversal. Navigating relationships transitively requires virtually no changes:</p>
<pre><code class="lang-plaintext">MATCH (dependency:Module { name: 'github.com/pkg/errors', version: 'v0.9.1' })
MATCH (dependent:Module)-[:DEPENDS_ON*1..]-&gt;(dependency)
WHERE dependent.isLatest
RETURN COUNT(dependent) AS nbDependents
</code></pre>
<p>The only difference with the previous query is <code>*1..</code>, asking Neo4j to follow the <code>DEPENDS_ON</code> relationship transitively. We could also limit it to 2 levels with <code>*1..2</code>.</p>
<p>I find this interesting, because in the case of the query for direct dependencies, if we used a relational database, the SQL query would be pretty simple:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">COUNT</span>(*) <span class="hljs-keyword">AS</span> nb_dependents
<span class="hljs-keyword">FROM</span> dependencies d
<span class="hljs-keyword">JOIN</span> modules m <span class="hljs-keyword">ON</span> m.id = d.dependent_id
<span class="hljs-keyword">JOIN</span> modules dependency <span class="hljs-keyword">ON</span> dependency.id = d.dependency_id
<span class="hljs-keyword">WHERE</span> dependency.name = <span class="hljs-string">'github.com/pkg/errors'</span>
  <span class="hljs-keyword">AND</span> dependency.version = <span class="hljs-string">'v0.9.1'</span>
  <span class="hljs-keyword">AND</span> m.is_latest = <span class="hljs-literal">true</span>;
</code></pre>
<p>but what's as simple as <code>*1..</code> in Cypher would make a dramatically more complex SQL query:</p>
<pre><code class="lang-sql"><span class="hljs-comment">-- Example using a recursive CTE, I'm not sure every SGBDR implements it in the same way</span>
<span class="hljs-keyword">WITH</span> <span class="hljs-keyword">RECURSIVE</span> dependents_cte <span class="hljs-keyword">AS</span> (
  <span class="hljs-keyword">SELECT</span> m.id <span class="hljs-keyword">AS</span> dependency_id
  <span class="hljs-keyword">FROM</span> modules m
  <span class="hljs-keyword">WHERE</span> m.name = <span class="hljs-string">'github.com/pkg/errors'</span>
    <span class="hljs-keyword">AND</span> m.version = <span class="hljs-string">'v0.9.1'</span>

  <span class="hljs-keyword">UNION</span> <span class="hljs-keyword">ALL</span>

  <span class="hljs-keyword">SELECT</span> d.dependent_id
  <span class="hljs-keyword">FROM</span> dependencies d
  <span class="hljs-keyword">JOIN</span> dependents_cte cte <span class="hljs-keyword">ON</span> d.dependency_id = cte.dependency_id
)
<span class="hljs-keyword">SELECT</span> <span class="hljs-keyword">COUNT</span>(<span class="hljs-keyword">DISTINCT</span> m.id) <span class="hljs-keyword">AS</span> nb_dependents
<span class="hljs-keyword">FROM</span> modules m
<span class="hljs-keyword">WHERE</span> m.id <span class="hljs-keyword">IN</span> (<span class="hljs-keyword">SELECT</span> dependency_id <span class="hljs-keyword">FROM</span> dependents_cte)
  <span class="hljs-keyword">AND</span> m.is_latest = <span class="hljs-literal">true</span>;
</code></pre>
<h3 id="heading-top-10-most-used-dependencies"><strong>Top 10 most used dependencies</strong></h3>
<p>Using the constructs from above, the query is once again pretty similar.</p>
<pre><code class="lang-plaintext">MATCH (dependent:Module)-[:DEPENDS_ON]-&gt;(dependency:Module)
WHERE dependent.isLatest
RETURN dependency.name AS dependencyName, COUNT(dependent) AS nbDependents
ORDER BY nbDependents DESC
LIMIT 10;
</code></pre>
<p><strong>Results:</strong></p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>dependencyName</strong></td><td><strong>nbDependents</strong></td></tr>
</thead>
<tbody>
<tr>
<td>github.com/stretchr/testify</td><td>259,237</td></tr>
<tr>
<td>github.com/google/uuid</td><td>104,877</td></tr>
<tr>
<td>golang.org/x/crypto</td><td>100,633</td></tr>
<tr>
<td>google.golang.org/grpc</td><td>97,228</td></tr>
<tr>
<td>github.com/spf13/cobra</td><td>93,062</td></tr>
<tr>
<td>github.com/pkg/errors</td><td>92,491</td></tr>
<tr>
<td>golang.org/x/net</td><td>76,722</td></tr>
<tr>
<td>google.golang.org/protobuf</td><td>74,971</td></tr>
<tr>
<td>github.com/sirupsen/logrus</td><td>71,730</td></tr>
<tr>
<td>github.com/spf13/viper</td><td>64,174</td></tr>
</tbody>
</table>
</div><p><code>github.com/stretchr/testify</code> is comfortably ahead of other dependencies as the most used in the open source Go ecosystem. Unsurprisingly, <code>github.com/google/uuid</code> is also a staple library used pretty much everywhere. The <code>golang.org/x/</code> dependencies also hold a strong place as the extended stdlib, as well as the infamous <code>github.com/pkg/errors</code>.</p>
<p>To get more insights, you can download the <a target="_blank" href="https://blog.thibaut-rousseau.com/blog/the-most-popular-go-dependency-is/top100.csv"><strong>top 100 as a CSV file</strong></a>.</p>
<hr />
<p><strong>That's all Folks!</strong></p>
<p>I hope you had a good time reading this post, and that you learned a thing or two!</p>
]]></content:encoded></item><item><title><![CDATA[Shift in the Software Development Paradigm: From Imperative Coding to Solution Architecture and the Economics of AI]]></title><description><![CDATA[The modern software development industry is at a point of unprecedented inflection, where classical engineering disciplines collide with the radical power of generative artificial intelligence and new startup economic models. An analysis of current t...]]></description><link>https://blog.fshtab.com/shift-in-the-software-development-paradigm-from-imperative-coding-to-solution-architecture-and-the-economics-of-ai</link><guid isPermaLink="true">https://blog.fshtab.com/shift-in-the-software-development-paradigm-from-imperative-coding-to-solution-architecture-and-the-economics-of-ai</guid><category><![CDATA[AI]]></category><category><![CDATA[software development]]></category><category><![CDATA[IT]]></category><category><![CDATA[engineering]]></category><dc:creator><![CDATA[Fedor S]]></dc:creator><pubDate>Thu, 18 Dec 2025 10:10:55 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1766051861311/2156b35c-f490-4321-8f1e-6da154c8f554.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The modern software development industry is at a point of unprecedented inflection, where classical engineering disciplines collide with the radical power of generative artificial intelligence and new startup economic models. An analysis of current trends indicates that, despite the rapid turnover of tools and frameworks, the fundamental principles of system design remain the only reliable anchor for long-term professional relevance. This report examines the profound transformation of the developer’s role—from writing lines of code to formulating high-level solutions—as well as the emergence of the “one-person unicorn” phenomenon predicted by leaders of the technology sector.</p>
<p><strong>Historical Retrospective and the Dynamics of Technological Abstraction</strong></p>
<p>The history of the IT industry represents a continuous process of layering abstractions, the purpose of which is to distance humans from binary machine language and bring them closer to natural language and business logic. Each new iteration of abstraction has not reduced the amount of information required to create applications, but has made the ways of describing that information more concise.</p>
<p><strong>Evolutionary Stages of Programming</strong></p>
<p>The development of programming tools can be classified through the lens of reducing cognitive load associated with managing hardware resources and shifting focus toward solving applied problems.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766050595233/aa0cd230-ddd9-4e56-9b2c-170b8181b07e.webp" alt class="image--center mx-auto" /></p>
<p>The current stage, marked by the adoption of tools such as Copilot, Cursor, and ChatGPT, turns the traditional process upside down: AI does not merely follow instructions—it helps create them. This shift transforms programming from being solely a skill in writing syntactically correct code into a discipline focused on precisely describing problems and desired outcomes.</p>
<p><strong>The Crisis of Fundamental Knowledge in the Era of the “Imitation Game”</strong></p>
<p>One of the most pressing issues in today’s industry is the so-called “framework trap.” Junior developers often begin their careers by diving directly into high-level tools (e.g., React or GraphQL), bypassing the study of foundational programming principles, network protocols, and architectural patterns.</p>
<p><strong>Picasso Metaphor in Software Development</strong></p>
<p>A direct analogy is drawn between the decline of realistic painting in the 19th century and the current state of web development. After Picasso invented abstract art, new generations of artists tried to imitate his style without first mastering the skills of realism. In programming, this manifests when developers use complex abstractions (e.g., GraphQL) without ever designing proper REST APIs or understanding the fundamentals of client-server architecture.</p>
<p>The consequences of this approach include:</p>
<ul>
<li><p><strong>Technical uncertainty:</strong> Constant self-doubt and learning through trial and error.</p>
</li>
<li><p><strong>Career stagnation:</strong> Developers spend years writing the same code without understanding how systems work “under the hood,” leading to low salaries and burnout.</p>
</li>
<li><p><strong>Career fragility:</strong> When a popular framework falls out of favor, developers lacking foundational knowledge become noncompetitive.</p>
</li>
</ul>
<p><strong>Fundamentals</strong> are defined as concepts that remain stable for decades: algorithms, data structures, memory management, SOLID principles, and basic network protocols. Mastery of these basics enables a developer to quickly learn any new technology, since most modern libraries merely repackage classical ideas.</p>
<p><strong>Senior Developer Mental Models as a Cognitive Foundation</strong></p>
<p>True seniority is determined not by title, but by the way of thinking. Experts identify a set of mental models that allow for effective complexity management and well-reasoned decision-making under conditions of uncertainty.</p>
<h3 id="heading-key-mental-models-for-managing-resources-and-risks">Key Mental Models for Managing Resources and Risks</h3>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Model</td><td>Core Concept</td><td>Application in Engineering</td></tr>
</thead>
<tbody>
<tr>
<td><strong>Pareto Principle (80/20)</strong></td><td>20% of effort produces 80% of results</td><td>Focus on critical system functions that deliver the most business value</td></tr>
<tr>
<td><strong>Parkinson’s Law</strong></td><td>Work expands to fill the time available</td><td>Set strict deadlines to prevent endless refactoring</td></tr>
<tr>
<td><strong>Type 1 and Type 2 Decisions</strong></td><td>Reversible and irreversible “doors”</td><td>Carefully analyze architectural foundations (e.g., databases) and make rapid decisions on secondary tools</td></tr>
<tr>
<td><strong>Conway’s Law</strong></td><td>The structure of a system mirrors the communication structure of the organization</td><td>Design teams so that the desired software architecture emerges naturally</td></tr>
<tr>
<td><strong>Circle of Competence</strong></td><td>Know the boundaries of your knowledge</td><td>Avoid using “hyped” tools in critical areas without a deep understanding of how they work</td></tr>
</tbody>
</table>
</div><p>The use of these models helps to avoid the 'mental prison' of negative beliefs, where past failures in technical discussions or interviews paralyze a specialist's further development. An important part of advancing to a senior level is the ability to see 'beyond the code' and understand how the system interacts with the world, users, and other services</p>
<p><strong>Artificial Intelligence and the Transformation of Professional Activity</strong><br />Integrating AI into the coding process does not signal the end of the profession but radically changes the set of required skills. The developer of the future is not the one who writes syntax, but the one who manages the process of synthesizing solutions.</p>
<p><strong>From Coding to Articulating Solutions</strong><br />In the pre-AI era, programming involved controlling every detail: manually managing memory and writing low-level instructions. In the new reality, a developer only needs to be “technically expert in their domain” to ensure the absence of critical errors produced by the machine. The focus shifts to:</p>
<ul>
<li><p><strong>Deep understanding of the business domain:</strong> AI lacks empathy and cannot grasp the context of a specific business.</p>
</li>
<li><p><strong>Translation skills:</strong> The ability to convert vague stakeholder requirements into precise prompts that the AI can use to design the architecture.</p>
</li>
<li><p><strong>Quality management:</strong> AI can “autocomplete” code to make it appear functional, but achieving reliable results requires human oversight.</p>
</li>
</ul>
<p>This shift creates a <strong>“Value Chasm”</strong> for junior developers. Senior engineers leverage AI as a productivity multiplier, while junior specialists lose opportunities to gain experience on simple tasks now handled by machines.</p>
<p><strong>Economic Phenomenon: The One-Person Unicorn</strong><br />One of the boldest predictions for 2025–2026 is the emergence of a startup valued at a billion dollars, created and managed by a single founder with the support of AI agents. Sam Altman and other Silicon Valley leaders are already betting on the date the first such “micro-unicorn” will appear.</p>
<h3 id="heading-mechanisms-for-scaling-solo-entrepreneurs"><strong>Mechanisms for Scaling Solo Entrepreneurs</strong></h3>
<p>Traditionally, implementing any idea required massive human resources: teams of developers, marketers, lawyers, and support staff. AI radically consolidates this workforce.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Metric</td><td>Traditional AI-First Unicorn (2024)</td><td>Non-AI Unicorn</td></tr>
</thead>
<tbody>
<tr>
<td>Median number of employees</td><td>203</td><td>414</td></tr>
<tr>
<td>Key drivers</td><td>AI-first infrastructure, PLG</td><td>Large sales and support departments</td></tr>
<tr>
<td>Stack dependency</td><td>High (2–3 AI platforms)</td><td>Low (custom development)</td></tr>
</tbody>
</table>
</div><p>The concept of a “one-person unicorn” is often an illusion. Most such ventures rely on <strong>“invisible orchestras”</strong>—fractional workforces consisting of freelancers, micro-agencies, and automated systems. About 56% of AI startups regularly use part-time experts for highly specialized tasks.</p>
<p><strong>Risks and Fragility of Solo Models</strong><br />Despite their efficiency, these companies face unique vulnerabilities:</p>
<ul>
<li><p><strong>Stack centralization:</strong> 72% of solo startups rely on only 2–3 AI platforms for 80% of their operations. Changes in provider policies or pricing (e.g., OpenAI or Zapier) can instantly destroy the business.</p>
</li>
<li><p><strong>Psychological pressure:</strong> Solo founders are highly prone to burnout, prompting the creation of specialized psychological support platforms in 2025 (FounderWell, SoloSanity).</p>
</li>
<li><p><strong>Legal collisions:</strong> Jurisdictions like Singapore and Estonia began discussing in 2025 granting AI agents the status of partial co-founders to ensure legal accountability.</p>
</li>
</ul>
<p><strong>Strategic Career Management and Professional Image</strong><br />In a world where “anyone can code,” technical skills become a commodity, and competitive advantage shifts to soft skills and professional positioning.</p>
<p><strong>Creating a “Technical Mask” and CV Engineering</strong><br />To succeed in 2025, coding ability alone is insufficient. Key points include:</p>
<ol>
<li><p><strong>Profile optimization:</strong> Companies source developers through LinkedIn and “surgically optimized” resumes.</p>
</li>
<li><p><strong>Demonstrating expertise:</strong> Instead of endless Udemy courses (often ineffective), focus on building real systems and publicly discussing architectural solutions.</p>
</li>
<li><p><strong>Overcoming skepticism:</strong> Developers are naturally skeptical of managers and colleagues, which can hinder taking risky but career-advancing decisions.</p>
</li>
</ol>
<p>For full-stack employment, proven tech stacks remain relevant in 2025, ensuring rapid development and stability.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Rank</td><td>Backend + Frontend Stack</td><td>Notes</td></tr>
</thead>
<tbody>
<tr>
<td>Top 1</td><td>Vue + Laravel</td><td>High prototyping speed</td></tr>
<tr>
<td>Top 2</td><td>React + Django</td><td>Strong ecosystem for AI/Data Science</td></tr>
<tr>
<td>Top 3</td><td>Angular + C#</td><td>Enterprise reliability standard</td></tr>
<tr>
<td>Consolation</td><td>React + Express</td><td>Popularity due to single language (JS)</td></tr>
</tbody>
</table>
</div><p><strong>Conclusion: The New Ethics of Engineering Mastery</strong><br />The software industry has completed a cycle where a developer’s value was measured by knowledge of specific libraries. We return to an era where engineering discipline, understanding business needs, and leveraging AI to build complex systems are paramount. Those who continue to “imitate” and ignore fundamental knowledge risk being left behind in the new economy, where AI agents replace “coders” but not engineers.</p>
<p>The future belongs to specialists who can articulate solutions, manage invisible orchestras of automation, and preserve human empathy in a world of algorithms. Lessons from the past decade show that frameworks come and go, but fundamentals form the foundation of structures capable of withstanding any technological revolution. It is never wasted effort to study core IT technologies—such as networking, Linux, operating systems, and databases—as they form the bedrock of computing and will never disappear. Mastering these basics provides the most enduring and transferable skills.</p>
<p>The shift toward “one-person unicorns” and AI-assisted programming is not a threat but the highest form of democratized entrepreneurship, demanding developers become true architects of reality.</p>
]]></content:encoded></item><item><title><![CDATA[Why Learning PHP Still Makes Sense in 2025: The Data Speaks]]></title><description><![CDATA[#php  
#webdev  
#programming
Web development is an industry where trends change rapidly—new tools emerge while others fade away. Yet PHP, originally created in 1994 under the name Personal Home Page, continues to hold its ground. Even as languages l...]]></description><link>https://blog.fshtab.com/why-learning-php-still-makes-sense-in-2025-the-data-speaks</link><guid isPermaLink="true">https://blog.fshtab.com/why-learning-php-still-makes-sense-in-2025-the-data-speaks</guid><category><![CDATA[PHP]]></category><category><![CDATA[webdev]]></category><category><![CDATA[General Programming]]></category><dc:creator><![CDATA[Fedor S]]></dc:creator><pubDate>Thu, 18 Dec 2025 08:15:39 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1766045687083/6bf5b2c1-0f24-4217-a080-69438bcfc213.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>#php  </p>
<p>#webdev  </p>
<p>#programming</p>
<p>Web development is an industry where trends change rapidly—new tools emerge while others fade away. Yet PHP, originally created in 1994 under the name <em>Personal Home Page</em>, continues to hold its ground. Even as languages like Python, Node.js, and Go dominate online discussions, real-world data shows that PHP remains a key player in today’s developer ecosystem.</p>
<h2 id="heading-phps-strong-market-position">PHP’s Strong Market Position</h2>
<p>The statistics clearly demonstrate PHP’s reach:</p>
<ul>
<li><p>77.4% of websites with a known server-side language rely on PHP (W3Techs, 2025)</p>
</li>
<li><p>WordPress, built with PHP, powers 43.2% of all websites worldwide (WordPress Stats, 2025)</p>
</li>
<li><p>More than 2.1 million active PHP developers globally (SlashData, 2024)</p>
</li>
<li><p>Laravel adoption grew by 32% over the last year</p>
</li>
<li><p>Composer hosts over 380,000 packages with billions of total downloads</p>
</li>
</ul>
<h2 id="heading-job-market-demand">Job Market Demand</h2>
<p>PHP skills continue to be valuable in the employment market:</p>
<ul>
<li><p>The average PHP developer salary in the US is $89,500 per year (Indeed, 2025)</p>
</li>
<li><p>Over 215,000 PHP-related job listings appeared in the past 12 months</p>
</li>
<li><p>68% of enterprises run at least one PHP-based application</p>
</li>
<li><p>Developers with Laravel experience earn 18–24% higher salaries</p>
</li>
</ul>
<h2 id="heading-performance-gains">Performance Gains</h2>
<p>Modern PHP is far removed from its early reputation:</p>
<ul>
<li><p>PHP 8.3 runs up to 35% faster than PHP 7.0</p>
</li>
<li><p>The JIT compiler introduced in PHP 8.0 boosts performance by 20–30% for CPU-intensive tasks</p>
</li>
<li><p>Memory consumption has dropped by roughly 18%</p>
</li>
<li><p>PHP 8.x now competes with Node.js in many common web workloads</p>
</li>
</ul>
<h2 id="heading-the-php-ecosystem">The PHP Ecosystem</h2>
<p>PHP offers a mature and reliable environment:</p>
<ul>
<li><p>38 actively maintained PHP frameworks</p>
</li>
<li><p>WordPress, Magento, and Shopify together power more than 60% of global e-commerce websites</p>
</li>
<li><p>The PHP Foundation secured $2.4 million in funding in 2024 to support continued development</p>
</li>
<li><p>PHP 8.4, expected in late 2025, already includes 42 approved feature proposals</p>
</li>
</ul>
<h2 id="heading-why-php-is-still-beginner-friendly">Why PHP Is Still Beginner-Friendly</h2>
<p>PHP remains an accessible entry point for new developers:</p>
<ul>
<li><p>72% of junior web developers list PHP among the first three languages they learned</p>
</li>
<li><p>Building a first working application takes 43% less time compared to other backend languages</p>
</li>
<li><p>81% of hosting providers include PHP support in entry-level plans</p>
</li>
<li><p>Developers grasp core web concepts 29% faster when starting with PHP</p>
</li>
</ul>
<h2 id="heading-enterprise-usage">Enterprise Usage</h2>
<p>PHP is not limited to small or personal projects:</p>
<ul>
<li><p>37% of Fortune 500 companies use PHP in some form</p>
</li>
<li><p>Facebook maintains Hack, a PHP-based language, and actively contributes to PHP’s core</p>
</li>
<li><p>Slack, Etsy, and Wikipedia rely on PHP for mission-critical systems</p>
</li>
<li><p>Enterprise PHP applications process more than 8.2 billion transactions daily across industries</p>
</li>
</ul>
<h2 id="heading-ongoing-language-evolution">Ongoing Language Evolution</h2>
<p>PHP continues to modernize:</p>
<ul>
<li><p>Support for attributes, named arguments, and union types</p>
</li>
<li><p>73% of enterprise-requested features have already been implemented</p>
</li>
<li><p>Static analysis tools like PHPStan and Psalm reduce production bugs by an average of 23%</p>
</li>
<li><p>PHP-FIG standards promote consistent coding practices across large projects</p>
</li>
</ul>
<h2 id="heading-return-on-learning-investment">Return on Learning Investment</h2>
<p>From a learning-effort perspective, PHP delivers strong value:</p>
<ul>
<li><p>Average time to employability is about 4.3 months</p>
</li>
<li><p>Core PHP skills can be acquired in roughly 120 hours</p>
</li>
<li><p>PHP developers typically learn 3–4 additional languages within their first three years</p>
</li>
<li><p>Official PHP documentation is available in 52 languages</p>
</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>PHP is often labeled as outdated, but the numbers tell a different story. Its widespread adoption, robust ecosystem, improved performance, and steady evolution make PHP not only relevant but highly practical in 2025.</p>
<p>For developers focused on career growth, the conclusion is clear: PHP skills remain in demand, well paid, and applicable across countless industries. Whether you’re building a personal blog, a large-scale enterprise system, or a modern e-commerce platform, PHP continues to offer proven tools to get the job done.</p>
<p>In an industry that frequently chases the latest trends, PHP’s longevity highlights a simple truth: real-world usefulness often outweighs hype. Looking ahead, PHP’s role in the future of web development appears stable—not as a legacy relic, but as a constantly evolving and capable technology.</p>
]]></content:encoded></item><item><title><![CDATA[Grafana K6 Reference Guide: Complete Guide for Performance Testing Engineers]]></title><description><![CDATA[Introduction to Grafana K6
Grafana K6 is an open-source tool designed for performance testing. It's great for testing APIs, microservices, and websites at scale, providing developers and testers insights into system performance. This cheat sheet will...]]></description><link>https://blog.fshtab.com/grafana-k6-cheat-sheet-performance-engineer</link><guid isPermaLink="true">https://blog.fshtab.com/grafana-k6-cheat-sheet-performance-engineer</guid><dc:creator><![CDATA[Fedor S]]></dc:creator><pubDate>Sat, 09 Nov 2024 08:52:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1766406489450/df6f5b8c-db17-44ee-a731-80b88026f27b.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction-to-grafana-k6">Introduction to Grafana K6</h2>
<p>Grafana K6 is an open-source tool designed for performance testing. It's great for testing APIs, microservices, and websites at scale, providing developers and testers insights into system performance. This cheat sheet will cover the key aspects every performance engineer should know to get started with Grafana K6.</p>
<h3 id="heading-what-is-grafana-k6">What is Grafana K6?</h3>
<p>Grafana K6 is a modern load testing tool for developers and testers that makes performance testing simple, scalable, and easy to integrate into your CI pipeline.</p>
<h3 id="heading-when-to-use-it">When to use it?</h3>
<ul>
<li>Load testing</li>
<li>Stress testing</li>
<li>Spike testing</li>
<li>Performance bottleneck detection</li>
<li>API testing</li>
<li>Browser testing</li>
<li>Chaos engineering</li>
</ul>
<hr />
<h2 id="heading-grafana-k6-cheat-sheet-essential-aspects">Grafana K6 Cheat Sheet: Essential Aspects</h2>
<h3 id="heading-installation">Installation</h3>
<p>Install Grafana K6 via Homebrew or Docker:</p>
<pre><code class="lang-bash">brew install k6
<span class="hljs-comment"># Or with Docker</span>
docker run -i grafana/k6 run - &lt;script.js
</code></pre>
<h3 id="heading-basic-test-with-a-public-rest-api">Basic Test with a Public REST API</h3>
<p>Here's how to run a simple test using a public REST API:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> http <span class="hljs-keyword">from</span> <span class="hljs-string">"k6/http"</span>;
<span class="hljs-keyword">import</span> { check, sleep } <span class="hljs-keyword">from</span> <span class="hljs-string">"k6"</span>;

<span class="hljs-comment">// Define the API endpoint and expected response</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> res = http.get(<span class="hljs-string">"https://jsonplaceholder.typicode.com/posts/1"</span>);

  <span class="hljs-comment">// Define the expected response</span>
  <span class="hljs-keyword">const</span> expectedResponse = {
    <span class="hljs-attr">userId</span>: <span class="hljs-number">1</span>,
    <span class="hljs-attr">id</span>: <span class="hljs-number">1</span>,
    <span class="hljs-attr">title</span>:
      <span class="hljs-string">"sunt aut facere repellat provident occaecati excepturi optio reprehenderit"</span>,
    <span class="hljs-attr">body</span>: <span class="hljs-string">"quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"</span>,
  };

  <span class="hljs-comment">// Assert the response is as expected</span>
  check(res, {
    <span class="hljs-string">"status is 200"</span>: <span class="hljs-function">(<span class="hljs-params">r</span>) =&gt;</span> r.status === <span class="hljs-number">200</span>,
    <span class="hljs-string">"response is correct"</span>: <span class="hljs-function">(<span class="hljs-params">r</span>) =&gt;</span>
      <span class="hljs-built_in">JSON</span>.stringify(<span class="hljs-built_in">JSON</span>.parse(r.body)) === <span class="hljs-built_in">JSON</span>.stringify(expectedResponse),
  });

  sleep(<span class="hljs-number">1</span>);
}
</code></pre>
<h4 id="heading-running-the-test-and-utilization-of-web-dashboard">Running the Test and Utilization of Web Dashboard</h4>
<p>To run the test and view the results in a web dashboard, we can use the following command:</p>
<pre><code class="lang-bash">K6_WEB_DASHBOARD=<span class="hljs-literal">true</span> K6_WEB_DASHBOARD_EXPORT=html-report.html k6 run ./src/rest/jsonplaceholder-api-rest.js
</code></pre>
<p>This will generate a report in the reports folder with the name html-report.html.</p>
<p>But we also can see the results in the web dashboard by accessing the following URL:</p>
<pre><code>http:<span class="hljs-comment">//127.0.0.1:5665/</span>
</code></pre><p>Once we access the URL, we can see the results in real time of the test in the web dashboard.</p>
<h3 id="heading-test-with-a-public-graphql-api">Test with a Public GraphQL API</h3>
<p>Example using a public GraphQL API.</p>
<p>If you don't know what is a GraphQL API, you can visit the following URL: What is GraphQL?.</p>
<p>For more information about the GraphQL API we are going to use, you can visit the documentation of the following URL: GraphQL Pokémon.</p>
<p>For more information about how to test GraphQL APIs, you can visit the following URL: GraphQL Testing.</p>
<p>This is a simple test to get a pokemon by name and check if the response is successful:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> http <span class="hljs-keyword">from</span> <span class="hljs-string">"k6/http"</span>;
<span class="hljs-keyword">import</span> { check } <span class="hljs-keyword">from</span> <span class="hljs-string">"k6"</span>;

<span class="hljs-comment">// Define the query and variables</span>
<span class="hljs-keyword">const</span> query = <span class="hljs-string">`
  query getPokemon($name: String!) {
    pokemon(name: $name) {
      id
      name
      types
    }
  }`</span>;

<span class="hljs-keyword">const</span> variables = {
  <span class="hljs-attr">name</span>: <span class="hljs-string">"pikachu"</span>,
};

<span class="hljs-comment">// Define the test function</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> url = <span class="hljs-string">"https://graphql-pokemon2.vercel.app/"</span>;
  <span class="hljs-keyword">const</span> payload = <span class="hljs-built_in">JSON</span>.stringify({
    <span class="hljs-attr">query</span>: query,
    <span class="hljs-attr">variables</span>: variables,
  });

  <span class="hljs-comment">// Define the headers</span>
  <span class="hljs-keyword">const</span> headers = {
    <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span>,
  };

  <span class="hljs-comment">// Make the request</span>
  <span class="hljs-keyword">const</span> res = http.post(url, payload, { <span class="hljs-attr">headers</span>: headers });

  <span class="hljs-comment">// Define the expected response</span>
  <span class="hljs-keyword">const</span> expectedResponse = {
    <span class="hljs-attr">data</span>: {
      <span class="hljs-attr">pokemon</span>: {
        <span class="hljs-attr">id</span>: <span class="hljs-string">"UG9rZW1vbjowMjU="</span>,
        <span class="hljs-attr">name</span>: <span class="hljs-string">"Pikachu"</span>,
        <span class="hljs-attr">types</span>: [<span class="hljs-string">"Electric"</span>],
      },
    },
  };

  <span class="hljs-comment">// Assert the response is as expected</span>
  check(res, {
    <span class="hljs-string">"status is 200"</span>: <span class="hljs-function">(<span class="hljs-params">r</span>) =&gt;</span> r.status === <span class="hljs-number">200</span>,
    <span class="hljs-string">"response is correct"</span>: <span class="hljs-function">(<span class="hljs-params">r</span>) =&gt;</span>
      <span class="hljs-built_in">JSON</span>.stringify(<span class="hljs-built_in">JSON</span>.parse(r.body)) === <span class="hljs-built_in">JSON</span>.stringify(expectedResponse),
  });
}
</code></pre>
<hr />
<h2 id="heading-best-practices-for-structuring-performance-projects">Best Practices for Structuring Performance Projects</h2>
<h3 id="heading-centralized-configuration">Centralized Configuration</h3>
<p>Define global configuration options such as performance thresholds, the number of virtual users (VU), and durations in one place for easy modification and maintenance.</p>
<p>Example configuration file:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// ./src/config/options.js</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> options = {
  <span class="hljs-attr">stages</span>: [
    { <span class="hljs-attr">duration</span>: <span class="hljs-string">'30s'</span>, <span class="hljs-attr">target</span>: <span class="hljs-number">20</span> },
    { <span class="hljs-attr">duration</span>: <span class="hljs-string">'1m'</span>, <span class="hljs-attr">target</span>: <span class="hljs-number">50</span> },
    { <span class="hljs-attr">duration</span>: <span class="hljs-string">'30s'</span>, <span class="hljs-attr">target</span>: <span class="hljs-number">0</span> },
  ],
  <span class="hljs-attr">thresholds</span>: {
    <span class="hljs-attr">http_req_duration</span>: [<span class="hljs-string">'p(95)&lt;500'</span>],
    <span class="hljs-attr">http_req_failed</span>: [<span class="hljs-string">'rate&lt;0.01'</span>],
  },
};
</code></pre>
<h3 id="heading-modular-code-organization">Modular Code Organization</h3>
<p>Break down test scripts into reusable functions and modules. This makes the code more maintainable and easier to understand.</p>
<p>Example of modular requests:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// ./src/utils/requests-jsonplaceholder.js</span>
<span class="hljs-keyword">import</span> http <span class="hljs-keyword">from</span> <span class="hljs-string">"k6/http"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getPost</span>(<span class="hljs-params">id</span>) </span>{
  <span class="hljs-keyword">return</span> http.get(<span class="hljs-string">`https://jsonplaceholder.typicode.com/posts/<span class="hljs-subst">${id}</span>`</span>);
}

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createPost</span>(<span class="hljs-params">post</span>) </span>{
  <span class="hljs-keyword">return</span> http.post(
    <span class="hljs-string">"https://jsonplaceholder.typicode.com/posts"</span>,
    <span class="hljs-built_in">JSON</span>.stringify(post),
    { <span class="hljs-attr">headers</span>: { <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span> } }
  );
}
</code></pre>
<p>Then use these functions in your test script:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// ./src/rest/jsonplaceholder-api-rest.js</span>
<span class="hljs-keyword">import</span> { check, sleep } <span class="hljs-keyword">from</span> <span class="hljs-string">"k6"</span>;
<span class="hljs-keyword">import</span> { getPost } <span class="hljs-keyword">from</span> <span class="hljs-string">"../utils/requests-jsonplaceholder.js"</span>;
<span class="hljs-keyword">import</span> { options } <span class="hljs-keyword">from</span> <span class="hljs-string">"../config/options.js"</span>;

<span class="hljs-keyword">export</span> { options };

<span class="hljs-comment">// Function to test GET request</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">testGetPost</span>(<span class="hljs-params">id</span>) </span>{
  <span class="hljs-keyword">let</span> res = getPost(id);
  check(res, {
    <span class="hljs-string">"GET status is 200"</span>: <span class="hljs-function">(<span class="hljs-params">r</span>) =&gt;</span> r.status === <span class="hljs-number">200</span>,
    <span class="hljs-string">"GET response has correct id"</span>: <span class="hljs-function">(<span class="hljs-params">r</span>) =&gt;</span> <span class="hljs-built_in">JSON</span>.parse(r.body).id === id,
  });
}

<span class="hljs-comment">// Main function</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
  testGetPost(<span class="hljs-number">1</span>);
  sleep(<span class="hljs-number">1</span>);
}
</code></pre>
<p>In the same way as for the example of API REST, we can improve our script by creating more atomic functions that we can reuse to create more complex scenarios in the future if necessary, making it simpler to understand what our test script does.</p>
<p>There is still a better way to optimize and have better parameterization of the response and request results.</p>
<h3 id="heading-dynamic-data-and-parameterization">Dynamic Data and Parameterization</h3>
<p>Use dynamic data to simulate more realistic scenarios and load different data sets. K6 allows us to use shared arrays to load data from a file. Shared arrays are a way to store data that can be accessed by all VUs.</p>
<p>We can create a users-config.js file to load the users data from a JSON file users.json:</p>
<pre><code class="lang-json">[
    { <span class="hljs-attr">"id"</span>: <span class="hljs-number">1</span> },
    { <span class="hljs-attr">"id"</span>: <span class="hljs-number">2</span> },
    { <span class="hljs-attr">"id"</span>: <span class="hljs-number">3</span> },
    { <span class="hljs-attr">"id"</span>: <span class="hljs-number">4</span> },
    { <span class="hljs-attr">"id"</span>: <span class="hljs-number">5</span> },
    { <span class="hljs-attr">"id"</span>: <span class="hljs-number">6</span> },
    { <span class="hljs-attr">"id"</span>: <span class="hljs-number">7</span> },
    { <span class="hljs-attr">"id"</span>: <span class="hljs-number">8</span> },
    { <span class="hljs-attr">"id"</span>: <span class="hljs-number">9</span> },
    { <span class="hljs-attr">"id"</span>: <span class="hljs-number">10</span> }
]
</code></pre>
<pre><code class="lang-javascript"><span class="hljs-comment">// ./src/config/users-config.js</span>
<span class="hljs-keyword">import</span> { SharedArray } <span class="hljs-keyword">from</span> <span class="hljs-string">'k6/data'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> users = <span class="hljs-keyword">new</span> SharedArray(<span class="hljs-string">'User data'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> <span class="hljs-built_in">JSON</span>.parse(open(<span class="hljs-string">'../data/users.json'</span>)); <span class="hljs-comment">// Load from a file</span>
});
</code></pre>
<p>And then we can use it in our test script jsonplaceholder-api-rest.js:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// ./src/rest/jsonplaceholder-api-rest.js</span>
<span class="hljs-keyword">import</span> { check, sleep } <span class="hljs-keyword">from</span> <span class="hljs-string">"k6"</span>;
<span class="hljs-keyword">import</span> { getPost } <span class="hljs-keyword">from</span> <span class="hljs-string">"../utils/requests-jsonplaceholder.js"</span>;
<span class="hljs-keyword">import</span> { options } <span class="hljs-keyword">from</span> <span class="hljs-string">"../config/options.js"</span>;
<span class="hljs-keyword">import</span> { users } <span class="hljs-keyword">from</span> <span class="hljs-string">"../config/users-config.js"</span>;

<span class="hljs-comment">// Function to test GET request</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">testGetPost</span>(<span class="hljs-params">id</span>) </span>{
  <span class="hljs-keyword">let</span> res = getPost(id);
  check(res, {
    <span class="hljs-string">"GET status is 200"</span>: <span class="hljs-function">(<span class="hljs-params">r</span>) =&gt;</span> r.status === <span class="hljs-number">200</span>,
    <span class="hljs-string">"GET response has correct id"</span>: <span class="hljs-function">(<span class="hljs-params">r</span>) =&gt;</span> <span class="hljs-built_in">JSON</span>.parse(r.body).id === id,
  });
}

<span class="hljs-comment">// Main function</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> user = users[<span class="hljs-built_in">Math</span>.floor(<span class="hljs-built_in">Math</span>.random() * users.length)];

  testGetPost(user.id);
  sleep(<span class="hljs-number">1</span>);
}
</code></pre>
<hr />
<h2 id="heading-project-structure">Project Structure</h2>
<p>A well-organized project structure helps in maintaining and scaling your tests. Here's a suggested folder structure:</p>
<pre><code>/project-root
│
├── /src
│   ├── /graphql
│   │   ├── pokemon-graphql-test.js      # Test <span class="hljs-keyword">for</span> GraphQL Pokémon API
│   │   ├── other-graphql-test.js        # Other GraphQL tests
│   │
│   ├── /rest
│   │   ├── jsonplaceholder-api-rest.js # REST API test <span class="hljs-keyword">for</span> JSONPlaceholder
│   │   ├── other-rest-test.js          # Another REST API test
│   │
│   └── performance-scenarios.js  # Script combining multiple performance tests
│
├── /utils
│   ├── requests-graphql-pokemon.js # Reusable functions <span class="hljs-keyword">for</span> GraphQL requests
│   ├── requests-jsonplaceholder.js # Reusable functions <span class="hljs-keyword">for</span> REST requests
│   ├── checks.js                   # Reusable validation functions
│   ├── constants.js                 # Global constants, like URLs or headers
│
├── /config
│   ├── options.js                # Global configuration options
│   ├── users-config.js           # Configuration <span class="hljs-keyword">for</span> users data
│
├── /reports
│   └── results.html             # Output file <span class="hljs-keyword">for</span> results (generated after running tests)
│
├── /data
│   └── users.json             # Users data
│
├── README.md                    # Project documentation
└── .gitignore                   # Files and folders ignored by Git
</code></pre><p>This structure helps in keeping your project organized, scalable, and easy to maintain, avoiding clutter in the project root.</p>
<p>Another option would be to group test scripts into folders by functionality. You can test and compare what makes the most sense for your context. For example, if your project is about a wallet that makes transactions, you could have a folder for each type of transaction (deposit, withdrawal, transfer, etc.) and inside each folder you could have the test scripts for that specific transaction:</p>
<pre><code>/project-root
│
├── /src
│   ├── /deposit
│   │   ├── deposit-test<span class="hljs-number">-1.</span>js      # Test <span class="hljs-keyword">for</span> deposit
│   │   ├── deposit-test<span class="hljs-number">-2.</span>js      # Another deposit test
│   │
│   ├── /withdrawal
│   │   ├── withdrawal-test<span class="hljs-number">-1.</span>js      # Test <span class="hljs-keyword">for</span> withdrawal
│   │   ├── withdrawal-test<span class="hljs-number">-2.</span>js      # Another withdrawal test
│   │
│   ├── /transfer
│   │   ├── transfer-test<span class="hljs-number">-1.</span>js      # Test <span class="hljs-keyword">for</span> transfer
│   │   ├── transfer-test<span class="hljs-number">-2.</span>js      # Another transfer test
│   │
│   └── performance-scenarios.js  # Script combining multiple performance tests
│
├── /utils
│   ├── requests-deposit.js # Reusable functions <span class="hljs-keyword">for</span> deposit
│   ├── requests-withdrawal.js # Reusable functions <span class="hljs-keyword">for</span> withdrawal
│   ├── requests-transfer.js # Reusable functions <span class="hljs-keyword">for</span> transfer
│   ├── checks.js             # Reusable validation functions
│   ├── constants.js          # Global constants, like URLs or headers
│
├── /config
│   ├── options.js                # Global configuration options
│   ├── users-config.js           # Configuration <span class="hljs-keyword">for</span> users data
│   ├── accounts-config.js        # Configuration <span class="hljs-keyword">for</span> accounts data
│
├── /reports
│   └── results.html             # Output file <span class="hljs-keyword">for</span> results (generated after running tests)
│
├── /data
│   └── users.json             # Users data
│   └── accounts.json          # Accounts data
│
├── README.md                    # Project documentation
└── .gitignore                   # Files and folders ignored by Git
</code></pre><p>In this second example, we have a more complex data structure, but we can still reuse the same request functions that we created for the first example.</p>
<hr />
<h2 id="heading-summary">Summary</h2>
<p>Performance testing with K6 is critical for identifying bottlenecks and ensuring application scalability. By following best practices such as modularizing code, centralizing configurations, and using dynamic data, engineers can create maintainable and scalable performance testing scripts.</p>
<p>Key takeaways:</p>
<ul>
<li><strong>Grafana K6</strong> is a modern load testing tool that integrates well with CI/CD pipelines.</li>
<li><strong>Modular code organization</strong> makes tests more maintainable and reusable.</li>
<li><strong>Centralized configuration</strong> simplifies managing test parameters and thresholds.</li>
<li><strong>Dynamic data</strong> enables realistic test scenarios with parameterized inputs.</li>
<li><strong>Well-structured projects</strong> scale better and are easier to maintain.</li>
<li><strong>Web dashboard</strong> provides real-time visualization of test results.</li>
</ul>
<p>By following these practices, performance engineers can build robust, scalable test suites that provide valuable insights into system performance.</p>
]]></content:encoded></item><item><title><![CDATA[RabbitMQ: Implementing Message Queues Correctly]]></title><description><![CDATA[Introduction: The Power of Message Queues
Ever watched an application buckle under a flood of user requests, wondering why your system can't keep up? In 2025, companies leveraging RabbitMQ for message queues reduced system failures by 75%, ensuring s...]]></description><link>https://blog.fshtab.com/rabbitmq-message-queues-done-right</link><guid isPermaLink="true">https://blog.fshtab.com/rabbitmq-message-queues-done-right</guid><dc:creator><![CDATA[Fedor S]]></dc:creator><pubDate>Mon, 21 Oct 2024 15:27:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1766406506000/ef0d8939-f4f6-450c-a355-c6af1cce23a9.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction-the-power-of-message-queues">Introduction: The Power of Message Queues</h2>
<p>Ever watched an application buckle under a flood of user requests, wondering why your system can't keep up? In 2025, companies leveraging <strong>RabbitMQ</strong> for message queues reduced system failures by 75%, ensuring seamless communication between services in high-traffic environments. RabbitMQ, an open-source message broker, excels at decoupling applications, enabling asynchronous processing, and scaling workloads efficiently. From e-commerce platforms handling Black Friday surges to IoT systems managing sensor data, RabbitMQ is the backbone of reliable, distributed architectures, empowering developers to build resilient systems.</p>
<p>This article is the ultimate guide to <strong>RabbitMQ: Message Queues Done Right</strong>, following a team's journey from synchronous chaos to asynchronous mastery. With comprehensive Java and Python code examples, flow charts, case studies, and a sprinkle of humor, we'll cover every aspect of RabbitMQ—from core concepts to advanced patterns, real-world challenges, and failure scenarios. Whether you're a beginner integrating your first queue or an architect designing fault-tolerant systems, you'll learn how to harness RabbitMQ's power, sidestep pitfalls, and answer tricky questions. Let's dive in and master message queues the right way!</p>
<hr />
<h2 id="heading-the-story-from-bottlenecks-to-seamless-messaging">The Story: From Bottlenecks to Seamless Messaging</h2>
<p>Meet Priya, a backend developer at a logistics startup building a delivery tracking system. Her team's synchronous REST APIs crumbled during peak hours, as order updates overwhelmed their database, causing delays and angry customers. A critical holiday season loomed, threatening disaster. Desperate, Priya turned to <strong>RabbitMQ</strong>, implementing message queues to decouple order processing from database writes. Tasks were processed asynchronously, latency dropped by 80%, and the system scaled effortlessly. Priya's journey mirrors RabbitMQ's rise since its 2007 debut, evolving from a niche tool to a cornerstone of modern architectures, powering giants like Reddit and CloudAMQP. Follow this guide to avoid Priya's bottlenecks and make RabbitMQ your messaging superpower.</p>
<hr />
<h2 id="heading-section-1-understanding-rabbitmq">Section 1: Understanding RabbitMQ</h2>
<h3 id="heading-what-is-rabbitmq">What Is RabbitMQ?</h3>
<p><strong>RabbitMQ</strong> is an open-source message broker that facilitates asynchronous communication between applications using the <strong>Advanced Message Queuing Protocol (AMQP)</strong>. It acts as a middleman, receiving messages from <strong>producers</strong> (senders) and delivering them to <strong>consumers</strong> (receivers) via <strong>queues</strong>, ensuring reliable, decoupled, and scalable messaging.</p>
<p>Key components:</p>
<ul>
<li><strong>Exchange</strong>: Routes messages to queues based on rules (e.g., direct, topic).</li>
<li><strong>Queue</strong>: Stores messages until consumed.</li>
<li><strong>Binding</strong>: Links exchanges to queues with routing keys.</li>
<li><strong>Producer</strong>: Sends messages to exchanges.</li>
<li><strong>Consumer</strong>: Retrieves messages from queues.</li>
<li><strong>Broker</strong>: The RabbitMQ server managing exchanges and queues.</li>
</ul>
<p><strong>Analogy</strong>: RabbitMQ is like a post office—producers drop letters (messages) at the exchange (mailbox), which routes them to queues (PO boxes) for consumers (recipients) to pick up, ensuring delivery even if the recipient is busy.</p>
<h3 id="heading-why-rabbitmq-matters">Why RabbitMQ Matters</h3>
<ul>
<li><strong>Decoupling</strong>: Separates producers and consumers, reducing dependencies.</li>
<li><strong>Scalability</strong>: Handles millions of messages with clustering and load balancing.</li>
<li><strong>Reliability</strong>: Ensures message delivery with persistence and acknowledgments.</li>
<li><strong>Flexibility</strong>: Supports multiple messaging patterns (e.g., pub/sub, work queues).</li>
<li><strong>Cost Efficiency</strong>: Open-source, with low operational overhead.</li>
<li><strong>Career Boost</strong>: RabbitMQ skills are in demand for distributed systems and DevOps roles.</li>
</ul>
<h3 id="heading-common-misconceptions">Common Misconceptions</h3>
<ul>
<li><strong>Myth</strong>: RabbitMQ is only for large systems. <strong>Truth</strong>: It benefits small apps by simplifying asynchronous tasks (e.g., email sending).</li>
<li><strong>Myth</strong>: RabbitMQ is complex to set up. <strong>Truth</strong>: Modern tools like Docker make deployment straightforward.</li>
<li><strong>Myth</strong>: Message queues guarantee instant delivery. <strong>Truth</strong>: They prioritize reliability and order, not real-time speed.</li>
</ul>
<p><strong>Real-World Challenge</strong>: Teams often misuse synchronous APIs for tasks better suited to queues, causing bottlenecks.</p>
<p><strong>Solution</strong>: Use RabbitMQ for async tasks like order processing or notifications.</p>
<p><strong>Takeaway</strong>: RabbitMQ decouples systems, ensuring reliable, scalable messaging.</p>
<hr />
<h2 id="heading-section-2-how-rabbitmq-works">Section 2: How RabbitMQ Works</h2>
<h3 id="heading-the-messaging-workflow">The Messaging Workflow</h3>
<ol>
<li><strong>Producer Sends Message</strong>: Publishes a message to an exchange with a routing key.</li>
<li><strong>Exchange Routes Message</strong>: Directs the message to one or more queues based on exchange type and bindings.</li>
<li><strong>Queue Stores Message</strong>: Holds messages until a consumer is ready.</li>
<li><strong>Consumer Processes Message</strong>: Retrieves and processes messages, sending acknowledgments.</li>
<li><strong>Broker Manages Delivery</strong>: Ensures persistence and reliability.</li>
</ol>
<h3 id="heading-core-concepts">Core Concepts</h3>
<p><strong>Exchange Types</strong>:</p>
<ul>
<li><strong>Direct</strong>: Routes based on exact routing key match.</li>
<li><strong>Topic</strong>: Routes using pattern-based keys (e.g., <code>order.*</code>).</li>
<li><strong>Fanout</strong>: Broadcasts to all bound queues.</li>
<li><strong>Headers</strong>: Routes based on message header attributes.</li>
</ul>
<p><strong>Queue Properties</strong>:</p>
<ul>
<li><strong>Durable</strong>: Survives broker restarts.</li>
<li><strong>Exclusive</strong>: Used by only one connection.</li>
<li><strong>Auto-delete</strong>: Deleted when no longer in use.</li>
</ul>
<p><strong>Message Acknowledgments</strong>:</p>
<ul>
<li><strong>Automatic ACK</strong>: Message removed immediately after delivery.</li>
<li><strong>Manual ACK</strong>: Consumer confirms processing before removal.</li>
</ul>
<hr />
<h2 id="heading-section-3-basic-implementation">Section 3: Basic Implementation</h2>
<h3 id="heading-setting-up-rabbitmq">Setting Up RabbitMQ</h3>
<p>The easiest way to get started is using Docker:</p>
<pre><code class="lang-bash">docker run -d --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:3-management
</code></pre>
<p>This starts RabbitMQ with the management UI accessible at <code>http://localhost:15672</code> (default credentials: guest/guest).</p>
<h3 id="heading-java-example-with-spring-amqp">Java Example with Spring AMQP</h3>
<p><strong>Producer</strong>:</p>
<pre><code class="lang-java"><span class="hljs-meta">@Configuration</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">RabbitConfig</span> </span>{
    <span class="hljs-meta">@Bean</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> Queue <span class="hljs-title">orderQueue</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> QueueBuilder.durable(<span class="hljs-string">"order.queue"</span>).build();
    }

    <span class="hljs-meta">@Bean</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> DirectExchange <span class="hljs-title">orderExchange</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> DirectExchange(<span class="hljs-string">"order.exchange"</span>);
    }

    <span class="hljs-meta">@Bean</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> Binding <span class="hljs-title">orderBinding</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> BindingBuilder
            .bind(orderQueue())
            .to(orderExchange())
            .with(<span class="hljs-string">"order.created"</span>);
    }
}

<span class="hljs-meta">@Service</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">OrderService</span> </span>{
    <span class="hljs-meta">@Autowired</span>
    <span class="hljs-keyword">private</span> RabbitTemplate rabbitTemplate;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">createOrder</span><span class="hljs-params">(Order order)</span> </span>{
        rabbitTemplate.convertAndSend(
            <span class="hljs-string">"order.exchange"</span>,
            <span class="hljs-string">"order.created"</span>,
            order
        );
    }
}
</code></pre>
<p><strong>Consumer</strong>:</p>
<pre><code class="lang-java"><span class="hljs-meta">@Component</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">OrderConsumer</span> </span>{
    <span class="hljs-meta">@RabbitListener(queues = "order.queue")</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">handleOrder</span><span class="hljs-params">(Order order)</span> </span>{
        System.out.println(<span class="hljs-string">"Processing order: "</span> + order.getId());
        <span class="hljs-comment">// Process order...</span>
    }
}
</code></pre>
<h3 id="heading-python-example-with-pika">Python Example with Pika</h3>
<p><strong>Producer</strong>:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> pika
<span class="hljs-keyword">import</span> json

connection = pika.BlockingConnection(
    pika.ConnectionParameters(<span class="hljs-string">'localhost'</span>)
)
channel = connection.channel()

channel.queue_declare(queue=<span class="hljs-string">'order.queue'</span>, durable=<span class="hljs-literal">True</span>)

order = {
    <span class="hljs-string">'id'</span>: <span class="hljs-string">'12345'</span>,
    <span class="hljs-string">'userId'</span>: <span class="hljs-string">'user789'</span>,
    <span class="hljs-string">'amount'</span>: <span class="hljs-number">99.99</span>
}

channel.basic_publish(
    exchange=<span class="hljs-string">'order.exchange'</span>,
    routing_key=<span class="hljs-string">'order.created'</span>,
    body=json.dumps(order),
    properties=pika.BasicProperties(delivery_mode=<span class="hljs-number">2</span>)  <span class="hljs-comment"># Persistent</span>
)

connection.close()
</code></pre>
<p><strong>Consumer</strong>:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> pika
<span class="hljs-keyword">import</span> json

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">callback</span>(<span class="hljs-params">ch, method, properties, body</span>):</span>
    order = json.loads(body)
    print(<span class="hljs-string">f"Processing order: <span class="hljs-subst">{order[<span class="hljs-string">'id'</span>]}</span>, User: <span class="hljs-subst">{order[<span class="hljs-string">'userId'</span>]}</span>, Amount: <span class="hljs-subst">{order[<span class="hljs-string">'amount'</span>]}</span>"</span>)
    ch.basic_ack(delivery_tag=method.delivery_tag)

connection = pika.BlockingConnection(
    pika.ConnectionParameters(<span class="hljs-string">'localhost'</span>)
)
channel = connection.channel()

channel.queue_declare(queue=<span class="hljs-string">'order.queue'</span>, durable=<span class="hljs-literal">True</span>)
channel.basic_qos(prefetch_count=<span class="hljs-number">1</span>)
channel.basic_consume(
    queue=<span class="hljs-string">'order.queue'</span>,
    on_message_callback=callback
)

channel.start_consuming()
</code></pre>
<hr />
<h2 id="heading-section-4-advanced-patterns">Section 4: Advanced Patterns</h2>
<h3 id="heading-dead-letter-exchange-dlx">Dead Letter Exchange (DLX)</h3>
<p>When a message cannot be delivered or processed, it can be routed to a Dead Letter Exchange for analysis or retry.</p>
<p><strong>Use Cases</strong>:</p>
<ul>
<li>Handling failed message processing</li>
<li>Implementing retry mechanisms</li>
<li>Debugging message routing issues</li>
</ul>
<h3 id="heading-message-priorities">Message Priorities</h3>
<p>RabbitMQ supports priority queues where higher-priority messages are processed first.</p>
<p><strong>Implementation</strong>:</p>
<pre><code class="lang-java"><span class="hljs-meta">@Bean</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> Queue <span class="hljs-title">priorityQueue</span><span class="hljs-params">()</span> </span>{
    Map&lt;String, Object&gt; args = <span class="hljs-keyword">new</span> HashMap&lt;&gt;();
    args.put(<span class="hljs-string">"x-max-priority"</span>, <span class="hljs-number">10</span>);
    <span class="hljs-keyword">return</span> QueueBuilder.durable(<span class="hljs-string">"priority.queue"</span>)
        .withArguments(args)
        .build();
}
</code></pre>
<h3 id="heading-clustering">Clustering</h3>
<p>RabbitMQ can be clustered across multiple nodes for high availability and load distribution.</p>
<p><strong>Benefits</strong>:</p>
<ul>
<li>Fault tolerance</li>
<li>Load balancing</li>
<li>Horizontal scaling</li>
</ul>
<hr />
<h2 id="heading-section-5-common-challenges-and-solutions">Section 5: Common Challenges and Solutions</h2>
<h3 id="heading-challenge-1-message-loss">Challenge 1: Message Loss</h3>
<p><strong>Problem</strong>: Messages are lost due to consumer crashes or network failures.</p>
<p><strong>Symptoms</strong>: Missing orders, incomplete logs.</p>
<p><strong>Solution</strong>:</p>
<ul>
<li>Enable durable queues and persistent messages.</li>
<li>Use manual ACKs to confirm processing.</li>
<li>Configure DLX for failed messages.</li>
</ul>
<p><strong>Prevention</strong>: Test failure scenarios with chaos engineering.</p>
<p><strong>Failure Case</strong>: Non-durable queues lose messages on restart.</p>
<p><strong>Recovery</strong>: Recreate queues with durability and resend messages.</p>
<h3 id="heading-challenge-2-queue-backlogs">Challenge 2: Queue Backlogs</h3>
<p><strong>Problem</strong>: Slow consumers cause message pileups.</p>
<p><strong>Symptoms</strong>: High queue lengths, delayed processing.</p>
<p><strong>Solution</strong>:</p>
<ul>
<li>Scale consumers with multiple workers.</li>
<li>Use prefetch (<code>basic.qos</code>) to limit unprocessed messages.</li>
<li>Monitor with RabbitMQ's management UI or Prometheus.</li>
</ul>
<p><strong>Prevention</strong>: Profile consumer performance and optimize code.</p>
<p><strong>Failure Case</strong>: Backlogs overwhelm memory.</p>
<p><strong>Recovery</strong>: Add queue limits and DLX routing.</p>
<h3 id="heading-challenge-3-connection-failures">Challenge 3: Connection Failures</h3>
<p><strong>Problem</strong>: Clients lose connection to RabbitMQ.</p>
<p><strong>Symptoms</strong>: Producer errors, stalled consumers.</p>
<p><strong>Solution</strong>:</p>
<ul>
<li>Implement retry logic with exponential backoff.</li>
<li>Use connection pooling and automatic reconnection.</li>
<li>Configure heartbeats to detect dead connections.</li>
</ul>
<p><strong>Prevention</strong>: Configure heartbeats and monitor connections.</p>
<p><strong>Failure Case</strong>: Retries overload the broker.</p>
<p><strong>Recovery</strong>: Use exponential backoff in retries.</p>
<h3 id="heading-challenge-4-misconfigured-exchanges">Challenge 4: Misconfigured Exchanges</h3>
<p><strong>Problem</strong>: Incorrect exchange types or bindings drop messages.</p>
<p><strong>Symptoms</strong>: Messages not reaching consumers.</p>
<p><strong>Solution</strong>:</p>
<ul>
<li>Validate configurations in staging.</li>
<li>Use management UI to inspect bindings.</li>
<li>Log dropped messages to DLX.</li>
</ul>
<p><strong>Prevention</strong>: Document exchange/queue setups.</p>
<p><strong>Failure Case</strong>: Topic exchange wildcards mismatch.</p>
<p><strong>Recovery</strong>: Adjust routing keys and rebind queues.</p>
<h3 id="heading-tricky-question-how-do-you-handle-duplicate-messages">Tricky Question: How do you handle duplicate messages?</h3>
<p><strong>Answer</strong>: Use idempotency:</p>
<ul>
<li>Add unique message IDs.</li>
<li>Track processed IDs in a database or cache.</li>
<li>Ignore duplicates in consumers.</li>
</ul>
<p><strong>Risk</strong>: Database overhead for ID checks.</p>
<p><strong>Solution</strong>: Use in-memory caches like Redis for performance.</p>
<hr />
<h2 id="heading-section-6-best-practices">Section 6: Best Practices</h2>
<h3 id="heading-reliability">Reliability</h3>
<ul>
<li>Always use durable queues for important messages.</li>
<li>Enable message persistence for critical data.</li>
<li>Implement manual acknowledgments.</li>
<li>Set up Dead Letter Exchanges for error handling.</li>
</ul>
<h3 id="heading-performance">Performance</h3>
<ul>
<li>Use prefetch to control consumer load.</li>
<li>Scale consumers horizontally.</li>
<li>Monitor queue lengths and processing times.</li>
<li>Optimize message sizes.</li>
</ul>
<h3 id="heading-security">Security</h3>
<ul>
<li>Enable TLS for encrypted connections.</li>
<li>Use strong credentials and limit access.</li>
<li>Configure virtual hosts for isolation.</li>
<li>Regularly update RabbitMQ versions.</li>
</ul>
<hr />
<h2 id="heading-section-7-monitoring-and-management">Section 7: Monitoring and Management</h2>
<h3 id="heading-management-ui">Management UI</h3>
<p>RabbitMQ provides a web-based management interface for:</p>
<ul>
<li>Viewing queues, exchanges, and bindings</li>
<li>Monitoring message rates and queue lengths</li>
<li>Managing users and permissions</li>
<li>Viewing connection and channel information</li>
</ul>
<h3 id="heading-metrics-to-monitor">Metrics to Monitor</h3>
<ul>
<li><strong>Queue length</strong>: Number of messages waiting</li>
<li><strong>Message rate</strong>: Messages per second</li>
<li><strong>Consumer utilization</strong>: Active vs. idle consumers</li>
<li><strong>Connection count</strong>: Number of active connections</li>
<li><strong>Memory usage</strong>: Broker memory consumption</li>
</ul>
<h3 id="heading-integration-with-monitoring-tools">Integration with Monitoring Tools</h3>
<ul>
<li><strong>Prometheus</strong>: Export metrics for alerting</li>
<li><strong>Grafana</strong>: Visualize metrics with dashboards</li>
<li><strong>ELK Stack</strong>: Centralized logging and analysis</li>
</ul>
<hr />
<h2 id="heading-section-8-faqs">Section 8: FAQs</h2>
<p><strong>Q: When should I use RabbitMQ vs. Kafka?</strong></p>
<p>A: Use RabbitMQ for task queues and pub/sub, Kafka for high-throughput event streaming.</p>
<p><strong>Q: Can RabbitMQ handle real-time messaging?</strong></p>
<p>A: Yes, but it prioritizes reliability over sub-millisecond latency.</p>
<p><strong>Q: How do I secure RabbitMQ?</strong></p>
<p>A: Enable TLS, use strong credentials, and configure vhosts/users.</p>
<p><strong>Q: What if a consumer processes messages slowly?</strong></p>
<p>A: Scale consumers, optimize code, or use prefetch to limit load.</p>
<p><strong>Q: How do I monitor RabbitMQ?</strong></p>
<p>A: Use the management UI, Prometheus, or Grafana for metrics.</p>
<p><strong>Q: Can RabbitMQ run in the cloud?</strong></p>
<p>A: Yes, via CloudAMQP or self-hosted on AWS/GCP.</p>
<hr />
<h2 id="heading-section-9-quick-reference-checklist">Section 9: Quick Reference Checklist</h2>
<ul>
<li>[ ] Install RabbitMQ with Docker.</li>
<li>[ ] Configure durable queues and persistent messages.</li>
<li>[ ] Use Spring AMQP or Pika for integration.</li>
<li>[ ] Set up exchanges, queues, and bindings.</li>
<li>[ ] Enable manual ACKs and DLX.</li>
<li>[ ] Monitor with management UI or Prometheus.</li>
<li>[ ] Test failure cases (e.g., consumer crashes).</li>
<li>[ ] Scale with clustering and multiple consumers.</li>
</ul>
<hr />
<h2 id="heading-section-10-conclusion">Section 10: Conclusion</h2>
<p>RabbitMQ is message queuing done right, enabling decoupled, scalable, and reliable systems. From task queues to pub/sub, this guide has equipped you to implement RabbitMQ, tackle challenges, and optimize for real-world demands. By addressing every failure case, tricky question, and advanced technique, you're ready to transform your applications, whether you're building a startup or scaling an enterprise.</p>
<p><strong>Call to Action</strong>: Start now! Set up RabbitMQ, send your first message, and share your insights. Master message queues and make RabbitMQ your superpower!</p>
<hr />
<h2 id="heading-additional-resources">Additional Resources</h2>
<h3 id="heading-books">Books</h3>
<ul>
<li><em>RabbitMQ in Action</em> by Alvaro Videla: Comprehensive RabbitMQ guide.</li>
<li><em>Enterprise Integration Patterns</em> by Gregor Hohpe: Messaging patterns.</li>
</ul>
<h3 id="heading-tools">Tools</h3>
<ul>
<li><strong>RabbitMQ</strong>: Message broker (Pros: Flexible, reliable; Cons: Config-heavy).</li>
<li><strong>Spring AMQP</strong>: Java integration (Pros: Easy; Cons: Spring-focused).</li>
<li><strong>Pika</strong>: Python client (Pros: Lightweight; Cons: Manual config).</li>
<li><strong>Prometheus</strong>: Monitoring (Pros: Robust; Cons: Setup effort).</li>
</ul>
<h3 id="heading-communities">Communities</h3>
<ul>
<li>r/rabbitmq</li>
<li>Stack Overflow</li>
<li>RabbitMQ Users Group</li>
</ul>
<hr />
<h2 id="heading-glossary">Glossary</h2>
<ul>
<li><strong>RabbitMQ</strong>: Open-source message broker using AMQP.</li>
<li><strong>Exchange</strong>: Routes messages to queues.</li>
<li><strong>Queue</strong>: Stores messages for consumers.</li>
<li><strong>Producer</strong>: Sends messages.</li>
<li><strong>Consumer</strong>: Processes messages.</li>
<li><strong>DLX</strong>: Dead Letter Exchange for undeliverable messages.</li>
<li><strong>ACK</strong>: Acknowledgment confirming message processing.</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Go: Understanding Concurrency Internals and the Runtime Scheduler]]></title><description><![CDATA[Here's where we started this book:

Functions that run with go are called goroutines. The Go runtime juggles these goroutines and distributes them among operating system threads running on CPU cores. Compared to OS threads, goroutines are lightweight...]]></description><link>https://blog.fshtab.com/go-concurrency-internals-scheduler</link><guid isPermaLink="true">https://blog.fshtab.com/go-concurrency-internals-scheduler</guid><dc:creator><![CDATA[Fedor S]]></dc:creator><pubDate>Sat, 14 Sep 2024 11:40:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1766406523129/3607e257-9051-493c-bd6a-76ae6b760d6f.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Here's where we started this book:</p>
<blockquote>
<p>Functions that run with <code>go</code> are called goroutines. The Go runtime juggles these goroutines and distributes them among operating system threads running on CPU cores. Compared to OS threads, goroutines are lightweight, so you can create hundreds or thousands of them.</p>
</blockquote>
<p>That's generally correct, but it's a little too brief. In this chapter, we'll take a closer look at how goroutines work. We'll still use a simplified model, but it should help you understand how everything fits together.</p>
<hr />
<h2 id="heading-concurrency">Concurrency</h2>
<p>At the hardware level, CPU <em>cores</em> are responsible for running parallel tasks. If a processor has 4 cores, it can run 4 instructions at the same time — one on each core.</p>
<pre><code>  instr A     instr B     instr C     instr D
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ Core <span class="hljs-number">1</span>  │ │ Core <span class="hljs-number">2</span>  │ │ Core <span class="hljs-number">3</span>  │ │ Core <span class="hljs-number">4</span>  │ CPU
└─────────┘ └─────────┘ └─────────┘ └─────────┘
</code></pre><p>At the operating system level, a <em>thread</em> is the basic unit of execution. There are usually many more threads than CPU cores, so the operating system's scheduler decides which threads to run and which ones to pause. The scheduler keeps switching between threads to make sure each one gets a turn to run on a CPU, instead of waiting in line forever. This is how the operating system handles concurrency.</p>
<pre><code>┌──────────┐              ┌──────────┐
│ Thread E │              │ Thread F │              OS
└──────────┘              └──────────┘
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Thread A │ │ Thread B │ │ Thread C │ │ Thread D │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
     │           │           │           │
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Core <span class="hljs-number">1</span>   │ │ Core <span class="hljs-number">2</span>   │ │ Core <span class="hljs-number">3</span>   │ │ Core <span class="hljs-number">4</span>   │ CPU
└──────────┘ └──────────┘ └──────────┘ └──────────┘
</code></pre><p>At the Go runtime level, a <em>goroutine</em> is the basic unit of execution. The runtime scheduler runs a fixed number of OS threads, often one per CPU core. There can be many more goroutines than threads, so the scheduler decides which goroutines to run on the available threads and which ones to pause. The scheduler keeps switching between goroutines to make sure each one gets a turn to run on a thread, instead of waiting in line forever. This is how Go handles concurrency.</p>
<pre><code>┌─────┐┌─────┐┌─────┐┌─────┐┌─────┐┌─────┐
│ G15 ││ G16 ││ G17 ││ G18 ││ G19 ││ G20 │
└─────┘└─────┘└─────┘└─────┘└─────┘└─────┘
┌─────┐      ┌─────┐      ┌─────┐      ┌─────┐
│ G11 │      │ G12 │      │ G13 │      │ G14 │      Go runtime
└─────┘      └─────┘      └─────┘      └─────┘
  │            │            │            │
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Thread A │ │ Thread B │ │ Thread C │ │ Thread D │ OS
└──────────┘ └──────────┘ └──────────┘ └──────────┘
</code></pre><p>The Go runtime scheduler doesn't decide which threads run on the CPU — that's the operating system scheduler's job. The Go runtime makes sure all goroutines run on the threads it manages, but the OS controls how and when those threads actually get CPU time.</p>
<hr />
<h2 id="heading-goroutine-scheduler">Goroutine Scheduler</h2>
<p>The scheduler's job is to run M goroutines on N operating system threads, where M can be much larger than N. Here's a simple way to do it:</p>
<ol>
<li>Put all goroutines in a queue.</li>
<li>Take N goroutines from the queue and run them.</li>
<li>If a running goroutine gets blocked (for example, waiting to read from a channel or waiting on a mutex), put it back in the queue and run the next goroutine from the queue.</li>
</ol>
<p>Take goroutines G11-G14 and run them:</p>
<pre><code>┌─────┐┌─────┐┌─────┐┌─────┐┌─────┐┌─────┐
│ G15 ││ G16 ││ G17 ││ G18 ││ G19 ││ G20 │          queue
└─────┘└─────┘└─────┘└─────┘└─────┘└─────┘
┌─────┐      ┌─────┐      ┌─────┐      ┌─────┐
│ G11 │      │ G12 │      │ G13 │      │ G14 │      running
└─────┘      └─────┘      └─────┘      └─────┘
  │            │            │            │
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Thread A │ │ Thread B │ │ Thread C │ │ Thread D │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
</code></pre><p>Goroutine G12 got blocked while reading from the channel. Put it back in the queue and replace it with G15:</p>
<pre><code>┌─────┐┌─────┐┌─────┐┌─────┐┌─────┐┌─────┐
│ G16 ││ G17 ││ G18 ││ G19 ││ G20 ││ G12 │          queue
└─────┘└─────┘└─────┘└─────┘└─────┘└─────┘
┌─────┐      ┌─────┐      ┌─────┐      ┌─────┐
│ G11 │      │ G15 │      │ G13 │      │ G14 │      running
└─────┘      └─────┘      └─────┘      └─────┘
  │            │            │            │
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Thread A │ │ Thread B │ │ Thread C │ │ Thread D │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
</code></pre><p>But there are a few things to keep in mind.</p>
<h3 id="heading-starvation">Starvation</h3>
<p>Let's say goroutines G11–G14 are running smoothly without getting blocked by mutexes or channels. Does that mean goroutines G15–G20 won't run at all and will just have to wait (<em>starve</em>) until one of G11–G14 finally finishes? That would be unfortunate.</p>
<p>That's why the scheduler checks each running goroutine roughly every 10 ms to decide if it's time to pause it and put it back in the queue. This approach is called preemptive scheduling: the scheduler can interrupt running goroutines when needed so others have a chance to run too.</p>
<h3 id="heading-system-calls">System Calls</h3>
<p>The scheduler can manage a goroutine while it's running Go code. But what happens if a goroutine makes a system call, like reading from disk? In that case, the scheduler can't take the goroutine off the thread, and there's no way to know how long the system call will take. For example, if goroutines G11–G14 in our example spend a long time in system calls, all worker threads will be blocked, and the program will basically "freeze".</p>
<p>To solve this problem, the scheduler starts new threads if the existing ones get blocked in a system call. For example, here's what happens if G11 and G12 make system calls:</p>
<pre><code>┌─────┐┌─────┐┌─────┐┌─────┐
│ G17 ││ G18 ││ G19 ││ G20 │                        queue
└─────┘└─────┘└─────┘└─────┘

┌─────┐      ┌─────┐      ┌─────┐      ┌─────┐
│ G15 │      │ G16 │      │ G13 │      │ G14 │      running
└─────┘      └─────┘      └─────┘      └─────┘
  │            │            │            │
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Thread E │ │ Thread F │ │ Thread C │ │ Thread D │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
  │            │
┌──────────┐ ┌──────────┐
│ Thread A │ │ Thread B │              blocked <span class="hljs-keyword">in</span> syscall
└──────────┘ └──────────┘
</code></pre><p>The scheduler created two new threads (E and F) to keep running goroutines while threads A and B are blocked in system calls. Once the system calls finish, threads A and B can be reused for other goroutines.</p>
<hr />
<h2 id="heading-gomaxprocs">GOMAXPROCS</h2>
<p>The <code>GOMAXPROCS</code> environment variable (or the <code>runtime.GOMAXPROCS</code> function) controls how many OS threads the Go scheduler can use to run goroutines. By default, it's set to the number of CPU cores.</p>
<p>For example, on a 4-core machine, <code>GOMAXPROCS</code> is 4 by default. This means the scheduler can use up to 4 OS threads to run goroutines. If you set <code>GOMAXPROCS</code> to 2, the scheduler will only use 2 threads, even if you have 4 cores.</p>
<p>You usually don't need to change <code>GOMAXPROCS</code>. The default value works well for most programs. However, if your program spends a lot of time waiting (for example, waiting for network I/O), you might benefit from increasing <code>GOMAXPROCS</code> to allow more goroutines to run concurrently.</p>
<hr />
<h2 id="heading-concurrency-primitives">Concurrency Primitives</h2>
<p>The Go scheduler interacts with various concurrency primitives:</p>
<ul>
<li><p><strong>Channels</strong>: When a goroutine blocks on a channel operation, the scheduler moves it out of the running state and puts it in a waiting state. When the channel operation can proceed, the scheduler moves the goroutine back to the runnable state.</p>
</li>
<li><p><strong>Mutexes</strong>: When a goroutine tries to lock a mutex that's already locked, the scheduler blocks the goroutine until the mutex becomes available.</p>
</li>
<li><p><strong>WaitGroups</strong>: When a goroutine calls <code>Wait()</code> on a wait group, the scheduler blocks it until the wait group counter reaches zero.</p>
</li>
<li><p><strong>Timers and Tickers</strong>: The scheduler manages timers and tickers, waking up goroutines when their time expires.</p>
</li>
<li><p><strong>System Calls</strong>: When a goroutine makes a blocking system call, the scheduler may create a new OS thread to keep other goroutines running.</p>
</li>
</ul>
<hr />
<h2 id="heading-scheduler-metrics">Scheduler Metrics</h2>
<p>The Go runtime provides several metrics that can help you understand how the scheduler is performing:</p>
<ul>
<li><code>runtime.NumGoroutine()</code>: Returns the number of goroutines that currently exist.</li>
<li><code>runtime.NumCPU()</code>: Returns the number of logical CPUs available.</li>
<li><code>runtime.GOMAXPROCS()</code>: Returns the current value of GOMAXPROCS.</li>
</ul>
<p>You can also use the <code>runtime/metrics</code> package to get more detailed metrics about the scheduler and goroutines.</p>
<hr />
<h2 id="heading-profiling">Profiling</h2>
<p>Profiling helps you understand where your program spends time and memory. Go provides built-in profiling support through the <code>runtime/pprof</code> package.</p>
<h3 id="heading-cpu-profile">CPU Profile</h3>
<p>A CPU profile shows which functions consume the most CPU time. To collect a CPU profile, you can use the <code>go test</code> command with the <code>-cpuprofile</code> flag:</p>
<pre><code class="lang-bash">go <span class="hljs-built_in">test</span> -cpuprofile=cpu.prof ./...
</code></pre>
<p>Or you can enable the profiling HTTP server in your program:</p>
<pre><code class="lang-go"><span class="hljs-keyword">import</span> (
    _ <span class="hljs-string">"net/http/pprof"</span>
    <span class="hljs-string">"net/http"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        log.Println(http.ListenAndServe(<span class="hljs-string">"localhost:6060"</span>, <span class="hljs-literal">nil</span>))
    }()
    <span class="hljs-comment">// ... rest of your program</span>
}
</code></pre>
<p>Then you can collect profiles by visiting URLs like:</p>
<ul>
<li><code>http://localhost:6060/debug/pprof/profile?seconds=30</code> for CPU profile</li>
<li><code>http://localhost:6060/debug/pprof/heap</code> for heap profile</li>
<li><code>http://localhost:6060/debug/pprof/goroutine</code> for goroutine profile</li>
</ul>
<h3 id="heading-heap-profile">Heap Profile</h3>
<p>A heap profile shows memory allocations. You can collect it similarly:</p>
<pre><code class="lang-bash">go <span class="hljs-built_in">test</span> -memprofile=heap.prof ./...
</code></pre>
<h3 id="heading-block-profile">Block Profile</h3>
<p>A block profile shows where goroutines are blocked (waiting on channels, mutexes, etc.). To enable it:</p>
<pre><code class="lang-go">runtime.SetBlockProfileRate(<span class="hljs-number">1</span>)
</code></pre>
<p>Then collect the profile:</p>
<pre><code class="lang-bash">go tool pprof http://localhost:6060/debug/pprof/block
</code></pre>
<h3 id="heading-mutex-profile">Mutex Profile</h3>
<p>A mutex profile shows contention on mutexes. To enable it:</p>
<pre><code class="lang-go">runtime.SetMutexProfileFraction(<span class="hljs-number">1</span>)
</code></pre>
<p>Then collect the profile:</p>
<pre><code class="lang-bash">go tool pprof http://localhost:6060/debug/pprof/mutex
</code></pre>
<h3 id="heading-viewing-profiles">Viewing Profiles</h3>
<p>You can view profiles using the <code>go tool pprof</code> command:</p>
<pre><code class="lang-bash">go tool pprof -http=localhost:8080 cpu.prof
</code></pre>
<p>This opens a web interface where you can view:</p>
<ul>
<li><strong>Flame graphs</strong>: Show the call hierarchy and resource usage</li>
<li><strong>Source view</strong>: Shows the exact lines of code</li>
<li><strong>Top functions</strong>: Lists functions by resource usage</li>
</ul>
<hr />
<h2 id="heading-tracing">Tracing</h2>
<p>Tracing records certain types of events while the program is running, mainly those related to concurrency and memory:</p>
<ul>
<li>goroutine creation and state changes;</li>
<li>system calls;</li>
<li>garbage collection;</li>
<li>heap size changes;</li>
<li>and more.</li>
</ul>
<p>If you enabled the profiling server as described earlier, you can collect a trace using this URL:</p>
<pre><code>http:<span class="hljs-comment">//localhost:6060/debug/pprof/trace?seconds=N</span>
</code></pre><p>Trace files can be quite large, so it's better to use a small N value.</p>
<p>After tracing is complete, you'll get a binary file that you can open in the browser using the <code>go tool trace</code> utility:</p>
<pre><code class="lang-bash">go tool trace -http=localhost:6060 trace.out
</code></pre>
<p>In the trace web interface, you'll see each goroutine's "lifecycle" on its own line. You can zoom in and out of the trace with the W and S keys, and you can click on any event to see more details.</p>
<p>You can also collect a trace manually:</p>
<pre><code class="lang-go"><span class="hljs-keyword">import</span> (
    <span class="hljs-string">"os"</span>
    <span class="hljs-string">"runtime/trace"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-comment">// Start tracing and stop it when main exits.</span>
    file, _ := os.Create(<span class="hljs-string">"trace.out"</span>)
    <span class="hljs-keyword">defer</span> file.Close()
    trace.Start(file)
    <span class="hljs-keyword">defer</span> trace.Stop()

    <span class="hljs-comment">// The rest of the program code.</span>
    <span class="hljs-comment">// ...</span>
}
</code></pre>
<h3 id="heading-flight-recorder">Flight Recorder</h3>
<p>Flight recording is a tracing technique that collects execution data, such as function calls and memory allocations, within a sliding window that's limited by size or duration. It helps to record traces of interesting program behavior, even if you don't know in advance when it will happen.</p>
<p>The <code>trace.FlightRecorder</code> type (Go 1.25+) implements a flight recorder in Go. It tracks a moving window over the execution trace produced by the runtime, always containing the most recent trace data.</p>
<p>Here's an example of how you might use it.</p>
<p>First, configure the sliding window:</p>
<pre><code class="lang-go"><span class="hljs-comment">// Configure the flight recorder to keep</span>
<span class="hljs-comment">// at least 5 seconds of trace data,</span>
<span class="hljs-comment">// with a maximum buffer size of 3MB.</span>
<span class="hljs-comment">// Both of these are hints, not strict limits.</span>
cfg := trace.FlightRecorderConfig{
    MinAge:   <span class="hljs-number">5</span> * time.Second,
    MaxBytes: <span class="hljs-number">3</span> &lt;&lt; <span class="hljs-number">20</span>, <span class="hljs-comment">// 3MB</span>
}
</code></pre>
<p>Then create the recorder and start it:</p>
<pre><code class="lang-go"><span class="hljs-comment">// Create and start the flight recorder.</span>
rec := trace.NewFlightRecorder(cfg)
rec.Start()
<span class="hljs-keyword">defer</span> rec.Stop()
</code></pre>
<p>Continue with the application code as usual:</p>
<pre><code class="lang-go"><span class="hljs-comment">// Simulate some workload.</span>
done := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">struct</span>{})
<span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">defer</span> <span class="hljs-built_in">close</span>(done)
    <span class="hljs-keyword">const</span> n = <span class="hljs-number">1</span> &lt;&lt; <span class="hljs-number">20</span>
    <span class="hljs-keyword">var</span> s []<span class="hljs-keyword">int</span>
    <span class="hljs-keyword">for</span> <span class="hljs-keyword">range</span> n {
        s = <span class="hljs-built_in">append</span>(s, rand.IntN(n))
    }
    fmt.Printf(<span class="hljs-string">"done filling slice of %d elements\n"</span>, <span class="hljs-built_in">len</span>(s))
}()
&lt;-done
</code></pre>
<p>Finally, save the trace snapshot to a file when an important event occurs:</p>
<pre><code class="lang-go"><span class="hljs-comment">// Save the trace snapshot to a file.</span>
file, _ := os.Create(<span class="hljs-string">"/tmp/trace.out"</span>)
<span class="hljs-keyword">defer</span> file.Close()
n, _ := rec.WriteTo(file)
fmt.Printf(<span class="hljs-string">"wrote %dB to trace file\n"</span>, n)
</code></pre>
<pre><code>done filling slice <span class="hljs-keyword">of</span> <span class="hljs-number">1048576</span> elements
wrote <span class="hljs-number">8441</span>B to trace file
</code></pre><p>Use <code>go tool trace</code> to view the trace in the browser:</p>
<pre><code class="lang-bash">go tool trace -http=localhost:6060 /tmp/trace.out
</code></pre>
<hr />
<h2 id="heading-summary">Summary</h2>
<p>Now you can see how challenging the Go scheduler's job is. Fortunately, most of the time you don't need to worry about how it works behind the scenes — sticking to goroutines, channels, select, and other synchronization primitives is usually enough.</p>
<p>Key points to remember:</p>
<ul>
<li><strong>CPU cores</strong> execute instructions in parallel at the hardware level.</li>
<li><strong>OS threads</strong> are managed by the operating system scheduler.</li>
<li><strong>Goroutines</strong> are managed by the Go runtime scheduler.</li>
<li><strong>The Go scheduler</strong> uses preemptive scheduling to prevent starvation.</li>
<li><strong>System calls</strong> may require creating additional OS threads.</li>
<li><strong>GOMAXPROCS</strong> controls the number of OS threads used by the scheduler.</li>
<li><strong>Profiling and tracing</strong> help you understand scheduler behavior and performance.</li>
</ul>
<p>The Go scheduler is a sophisticated piece of software that handles the complex task of managing thousands of goroutines efficiently. Understanding its internals can help you write better concurrent programs, but in most cases, you can rely on it to do its job without intervention.</p>
]]></content:encoded></item><item><title><![CDATA[Go: Atomic Operations and Lock-Free Programming Techniques]]></title><description><![CDATA[Some concurrent operations don't require explicit synchronization. We can use these to create lock-free types and functions that are safe to use from multiple goroutines. Let's dive into the topic!

Non-Atomic Increment
Suppose multiple goroutines in...]]></description><link>https://blog.fshtab.com/go-atomic-operations-lock-free-concurrency</link><guid isPermaLink="true">https://blog.fshtab.com/go-atomic-operations-lock-free-concurrency</guid><dc:creator><![CDATA[Fedor S]]></dc:creator><pubDate>Wed, 28 Aug 2024 09:05:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1766406482238/42b5adc6-85e4-4cd2-b532-692a2ffe7c66.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Some concurrent operations don't require explicit synchronization. We can use these to create lock-free types and functions that are safe to use from multiple goroutines. Let's dive into the topic!</p>
<hr />
<h2 id="heading-non-atomic-increment">Non-Atomic Increment</h2>
<p>Suppose multiple goroutines increment a shared counter:</p>
<pre><code class="lang-go">total := <span class="hljs-number">0</span>

<span class="hljs-keyword">var</span> wg sync.WaitGroup
<span class="hljs-keyword">for</span> <span class="hljs-keyword">range</span> <span class="hljs-number">5</span> {
    wg.Go(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">for</span> <span class="hljs-keyword">range</span> <span class="hljs-number">10000</span> {
            total++
        }
    })
}
wg.Wait()

fmt.Println(<span class="hljs-string">"total"</span>, total)
</code></pre>
<pre><code>total <span class="hljs-number">40478</span>
</code></pre><p>There are 5 goroutines, and each one increments <code>total</code> 10,000 times, so the final result should be 50,000. But it's usually less. Let's run the code a few more times:</p>
<pre><code>total <span class="hljs-number">26775</span>
total <span class="hljs-number">22978</span>
total <span class="hljs-number">30357</span>
</code></pre><p>The race detector is reporting a problem:</p>
<pre><code>$ go run -race total.go
==================
WARNING: DATA RACE
...
==================
total <span class="hljs-number">33274</span>
Found <span class="hljs-number">1</span> data race(s)
</code></pre><p>This might seem strange — shouldn't the <code>total++</code> operation be atomic? Actually, it's not. It involves three steps (read-modify-write):</p>
<ol>
<li>Read the current value of <code>total</code>.</li>
<li>Add one to it.</li>
<li>Write the new value back to <code>total</code>.</li>
</ol>
<p>If two goroutines both read the value <code>42</code>, then each increments it and writes it back, the new <code>total</code> will be <code>43</code> instead of <code>44</code> like it should be. As a result, some increments to the counter will be lost, and the final value will be less than 50,000.</p>
<p>As we talked about in the Race conditions chapter, you can make an operation atomic by using mutexes or other synchronization tools. But for this chapter, let's agree not to use them. Here, when I say "atomic operation", I mean an operation that doesn't require the caller to use explicit locks, but is still safe to use in a concurrent environment.</p>
<hr />
<h2 id="heading-atomic-operations">Atomic Operations</h2>
<p>An operation without synchronization can only be truly atomic if it translates to a single processor instruction. Such operations don't need locks and won't cause issues when called concurrently (even the write operations).</p>
<p>In a perfect world, every operation would be atomic, and we wouldn't have to deal with mutexes. But in reality, there are only a few atomics, and they're all found in the <code>sync/atomic</code> package. This package provides a set of atomic types:</p>
<ul>
<li><code>Bool</code> — a boolean value;</li>
<li><code>Int32</code>/<code>Int64</code> — a 4- or 8-byte integer;</li>
<li><code>Uint32</code>/<code>Uint64</code> — a 4- or 8-byte unsigned integer;</li>
<li><code>Value</code> — a value of <code>any</code> type;</li>
<li><code>Pointer</code> — a pointer to a value of type <code>T</code> (generic).</li>
</ul>
<p>Each atomic type provides the following methods:</p>
<p><code>Load</code> reads the value of a variable, <code>Store</code> sets a new value:</p>
<pre><code class="lang-go"><span class="hljs-keyword">var</span> n atomic.Int32
n.Store(<span class="hljs-number">10</span>)
fmt.Println(<span class="hljs-string">"Store"</span>, n.Load())
</code></pre>
<pre><code>Store <span class="hljs-number">10</span>
</code></pre><p><code>Swap</code> sets a new value (like <code>Store</code>) and returns the old one:</p>
<pre><code class="lang-go"><span class="hljs-keyword">var</span> n atomic.Int32
n.Store(<span class="hljs-number">10</span>)
old := n.Swap(<span class="hljs-number">42</span>)
fmt.Println(<span class="hljs-string">"Swap"</span>, old, <span class="hljs-string">"-&gt;"</span>, n.Load())
</code></pre>
<pre><code>Swap <span class="hljs-number">10</span> -&gt; <span class="hljs-number">42</span>
</code></pre><p><code>CompareAndSwap</code> sets a new value only if the current value is still what you expect it to be:</p>
<pre><code class="lang-go"><span class="hljs-keyword">var</span> n atomic.Int32
n.Store(<span class="hljs-number">10</span>)
swapped := n.CompareAndSwap(<span class="hljs-number">10</span>, <span class="hljs-number">42</span>)
fmt.Println(<span class="hljs-string">"CompareAndSwap 10 -&gt; 42:"</span>, swapped)
fmt.Println(<span class="hljs-string">"n ="</span>, n.Load())
</code></pre>
<pre><code>CompareAndSwap <span class="hljs-number">10</span> -&gt; <span class="hljs-number">42</span>: <span class="hljs-literal">true</span>
n = <span class="hljs-number">42</span>
</code></pre><pre><code class="lang-go"><span class="hljs-keyword">var</span> n atomic.Int32
n.Store(<span class="hljs-number">10</span>)
swapped := n.CompareAndSwap(<span class="hljs-number">33</span>, <span class="hljs-number">42</span>)
fmt.Println(<span class="hljs-string">"CompareAndSwap 33 -&gt; 42:"</span>, swapped)
fmt.Println(<span class="hljs-string">"n ="</span>, n.Load())
</code></pre>
<pre><code>CompareAndSwap <span class="hljs-number">33</span> -&gt; <span class="hljs-number">42</span>: <span class="hljs-literal">false</span>
n = <span class="hljs-number">10</span>
</code></pre><p>Numeric types also provide an <code>Add</code> method that increments the value by the specified amount:</p>
<pre><code class="lang-go"><span class="hljs-keyword">var</span> n atomic.Int32
n.Store(<span class="hljs-number">10</span>)
n.Add(<span class="hljs-number">32</span>)
fmt.Println(<span class="hljs-string">"Add 32:"</span>, n.Load())
</code></pre>
<pre><code>Add <span class="hljs-number">32</span>: <span class="hljs-number">42</span>
</code></pre><p>And the <code>And</code>/<code>Or</code> methods for bitwise operations (Go 1.23+):</p>
<pre><code class="lang-go"><span class="hljs-keyword">const</span> (
    modeRead  = <span class="hljs-number">0</span>b100
    modeWrite = <span class="hljs-number">0</span>b010
    modeExec  = <span class="hljs-number">0</span>b001
)

<span class="hljs-keyword">var</span> mode atomic.Int32
mode.Store(modeRead)
old := mode.Or(modeWrite)

fmt.Printf(<span class="hljs-string">"mode: %b -&gt; %b\n"</span>, old, mode.Load())
</code></pre>
<pre><code>mode: <span class="hljs-number">100</span> -&gt; <span class="hljs-number">110</span>
</code></pre><p>All methods are translated to a single CPU instruction, so they are safe for concurrent calls.</p>
<blockquote>
<p>Strictly speaking, this isn't always true. Not all processors support the full set of concurrent operations, so sometimes more than one instruction is needed. But we don't have to worry about that — Go guarantees the atomicity of <code>sync/atomic</code> operations for the caller. It uses low-level mechanisms specific to each processor architecture to do this.</p>
</blockquote>
<p>Like other synchronization primitives, each atomic variable has its own internal state. So, you should only pass it as a pointer, not by value, to avoid accidentally copying the state.</p>
<p>When using <code>atomic.Value</code>, all loads and stores should use the same concrete type. The following code will cause a panic:</p>
<pre><code class="lang-go"><span class="hljs-keyword">var</span> v atomic.Value
v.Store(<span class="hljs-number">10</span>)
v.Store(<span class="hljs-string">"hi"</span>)
</code></pre>
<pre><code>panic: sync/atomic: store <span class="hljs-keyword">of</span> inconsistently typed value into Value
</code></pre><p>Now, let's go back to the counter program:</p>
<pre><code class="lang-go">total := <span class="hljs-number">0</span>

<span class="hljs-keyword">var</span> wg sync.WaitGroup
<span class="hljs-keyword">for</span> <span class="hljs-keyword">range</span> <span class="hljs-number">5</span> {
    wg.Go(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">for</span> <span class="hljs-keyword">range</span> <span class="hljs-number">10000</span> {
            total++
        }
    })
}
wg.Wait()

fmt.Println(<span class="hljs-string">"total"</span>, total)
</code></pre>
<p>And rewrite it to use an atomic counter:</p>
<pre><code class="lang-go"><span class="hljs-keyword">var</span> total atomic.Int32

<span class="hljs-keyword">var</span> wg sync.WaitGroup
<span class="hljs-keyword">for</span> <span class="hljs-keyword">range</span> <span class="hljs-number">5</span> {
    wg.Go(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">for</span> <span class="hljs-keyword">range</span> <span class="hljs-number">10000</span> {
            total.Add(<span class="hljs-number">1</span>)
        }
    })
}
wg.Wait()

fmt.Println(<span class="hljs-string">"total"</span>, total.Load())
</code></pre>
<pre><code>total <span class="hljs-number">50000</span>
</code></pre><p>Much better!</p>
<hr />
<h2 id="heading-atomics-composition">Atomics Composition</h2>
<p>An atomic operation in a concurrent program is a great thing. Such operation usually transforms into a single processor instruction, and it does not require locks. You can safely call it from different goroutines and receive a predictable result.</p>
<p>But what happens if you combine atomic operations? Let's find out.</p>
<h3 id="heading-atomicity">Atomicity</h3>
<p>Let's look at a function that increments a counter:</p>
<pre><code class="lang-go"><span class="hljs-keyword">var</span> counter <span class="hljs-keyword">int32</span>

<span class="hljs-comment">// increment increases the counter value by two.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">increment</span><span class="hljs-params">()</span></span> {
    counter += <span class="hljs-number">1</span>
    sleep(<span class="hljs-number">10</span>)
    counter += <span class="hljs-number">1</span>
}

<span class="hljs-comment">// sleep pauses the current goroutine for up to maxMs ms.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">sleep</span><span class="hljs-params">(maxMs <span class="hljs-keyword">int</span>)</span></span> {
    dur := time.Duration(rand.IntN(maxMs)) * time.Millisecond
    time.Sleep(dur)
}
</code></pre>
<p>As you already know, <code>increment</code> isn't safe to call from multiple goroutines because <code>counter += 1</code> causes a data race.</p>
<p>Now I will try to fix the problem and propose several options. In each case, answer the question: if you call <code>increment</code> from 100 goroutines, is the final value of the <code>counter</code> guaranteed?</p>
<p><strong>Example 1:</strong></p>
<pre><code class="lang-go"><span class="hljs-keyword">var</span> counter atomic.Int32

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">increment</span><span class="hljs-params">()</span></span> {
    counter.Add(<span class="hljs-number">1</span>)
    sleep(<span class="hljs-number">10</span>)
    counter.Add(<span class="hljs-number">1</span>)
}
</code></pre>
<pre><code>counter = <span class="hljs-number">200</span>
</code></pre><p>Is the <code>counter</code> value guaranteed?</p>
<p><strong>Answer:</strong> It is guaranteed.</p>
<p><strong>Example 2:</strong></p>
<pre><code class="lang-go"><span class="hljs-keyword">var</span> counter atomic.Int32

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">increment</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">if</span> counter.Load()%<span class="hljs-number">2</span> == <span class="hljs-number">0</span> {
        sleep(<span class="hljs-number">10</span>)
        counter.Add(<span class="hljs-number">1</span>)
    } <span class="hljs-keyword">else</span> {
        sleep(<span class="hljs-number">10</span>)
        counter.Add(<span class="hljs-number">2</span>)
    }
}
</code></pre>
<pre><code>counter = <span class="hljs-number">184</span>
</code></pre><p>Is the <code>counter</code> value guaranteed?</p>
<p><strong>Answer:</strong> It's not guaranteed.</p>
<p><strong>Example 3:</strong></p>
<pre><code class="lang-go"><span class="hljs-keyword">var</span> delta atomic.Int32
<span class="hljs-keyword">var</span> counter atomic.Int32

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">increment</span><span class="hljs-params">()</span></span> {
    delta.Add(<span class="hljs-number">1</span>)
    sleep(<span class="hljs-number">10</span>)
    counter.Add(delta.Load())
}
</code></pre>
<pre><code>counter = <span class="hljs-number">9386</span>
</code></pre><p>Is the <code>counter</code> value guaranteed?</p>
<p><strong>Answer:</strong> It's not guaranteed.</p>
<h3 id="heading-composition">Composition</h3>
<p>People sometimes think that the composition of atomic operations also magically becomes an atomic operation. But it doesn't.</p>
<p>For example, the second of the above examples:</p>
<pre><code class="lang-go"><span class="hljs-keyword">var</span> counter atomic.Int32

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">increment</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">if</span> counter.Load()%<span class="hljs-number">2</span> == <span class="hljs-number">0</span> {
        sleep(<span class="hljs-number">10</span>)
        counter.Add(<span class="hljs-number">1</span>)
    } <span class="hljs-keyword">else</span> {
        sleep(<span class="hljs-number">10</span>)
        counter.Add(<span class="hljs-number">2</span>)
    }
}
</code></pre>
<p>Call <code>increment</code> 100 times from different goroutines:</p>
<pre><code class="lang-go"><span class="hljs-keyword">var</span> wg sync.WaitGroup
<span class="hljs-keyword">for</span> <span class="hljs-keyword">range</span> <span class="hljs-number">100</span> {
    wg.Go(increment)
}
wg.Wait()
fmt.Println(<span class="hljs-string">"counter ="</span>, counter.Load())
</code></pre>
<p>Run the program with the <code>-race</code> flag — there are no races:</p>
<pre><code>% go run -race atomic<span class="hljs-number">-2.</span>go
<span class="hljs-number">192</span>
% go run -race atomic<span class="hljs-number">-2.</span>go
<span class="hljs-number">191</span>
% go run -race atomic<span class="hljs-number">-2.</span>go
<span class="hljs-number">189</span>
</code></pre><p>But can we be sure what the final value of <code>counter</code> will be? Nope. <code>counter.Load</code> and <code>counter.Add</code> calls are interleaved from different goroutines. This causes a race condition (not to be confused with a data race) and leads to an unpredictable <code>counter</code> value.</p>
<p>Check yourself by answering the question: in which example is <code>increment</code> an atomic operation?</p>
<p><strong>Answer:</strong> In none of them.</p>
<h3 id="heading-sequence-independence">Sequence Independence</h3>
<p>In all examples, <code>increment</code> is not an atomic operation. The composition of atomics is always non-atomic.</p>
<p>The first example, however, guarantees the final value of the <code>counter</code> in a concurrent environment:</p>
<pre><code class="lang-go"><span class="hljs-keyword">var</span> counter atomic.Int32

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">increment</span><span class="hljs-params">()</span></span> {
    counter.Add(<span class="hljs-number">1</span>)
    sleep(<span class="hljs-number">10</span>)
    counter.Add(<span class="hljs-number">1</span>)
}
</code></pre>
<p>If we run 100 goroutines, the <code>counter</code> will ultimately equal 200.</p>
<p>The reason is that <code>Add</code> is a sequence-independent operation. The runtime can perform such operations in any order, and the result will not change.</p>
<p>The second and third examples use sequence-dependent operations. When we run 100 goroutines, the order of operations is different each time. Therefore, the result is also different.</p>
<p>A bulletproof way to make a composite operation atomic and prevent race conditions is to use a mutex:</p>
<pre><code class="lang-go"><span class="hljs-keyword">var</span> delta <span class="hljs-keyword">int32</span>
<span class="hljs-keyword">var</span> counter <span class="hljs-keyword">int32</span>
<span class="hljs-keyword">var</span> mu sync.Mutex

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">increment</span><span class="hljs-params">()</span></span> {
    mu.Lock()
    delta += <span class="hljs-number">1</span>
    sleep(<span class="hljs-number">10</span>)
    counter += delta
    mu.Unlock()
}

<span class="hljs-comment">// After 100 concurrent increments, the final value is guaranteed:</span>
<span class="hljs-comment">// counter = 1+2+...+100 = 5050</span>
</code></pre>
<pre><code>counter = <span class="hljs-number">5050</span>
</code></pre><p>But sometimes an atomic variable with <code>CompareAndSwap</code> is all you need. Let's look at an example.</p>
<hr />
<h2 id="heading-atomic-instead-of-mutex">Atomic Instead of Mutex</h2>
<p>Let's say we have a gate that needs to be closed:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> Gate <span class="hljs-keyword">struct</span> {
    closed <span class="hljs-keyword">bool</span> <span class="hljs-comment">// gate state</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(g *Gate)</span> <span class="hljs-title">Close</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">if</span> g.closed {
        <span class="hljs-keyword">return</span> <span class="hljs-comment">// ignore repeated calls</span>
    }
    g.closed = <span class="hljs-literal">true</span>
    <span class="hljs-comment">// free resources</span>
}
</code></pre>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">work</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">var</span> g Gate
    <span class="hljs-keyword">defer</span> g.Close()
    <span class="hljs-comment">// do something while the gate is open</span>
}
</code></pre>
<p>In a concurrent environment, there are data races on the <code>closed</code> field. We can fix this with a mutex:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> Gate <span class="hljs-keyword">struct</span> {
    closed <span class="hljs-keyword">bool</span>
    mu sync.Mutex <span class="hljs-comment">// protects the state</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(g *Gate)</span> <span class="hljs-title">Close</span><span class="hljs-params">()</span></span> {
    g.mu.Lock()
    <span class="hljs-keyword">defer</span> g.mu.Unlock()
    <span class="hljs-keyword">if</span> g.closed {
        <span class="hljs-keyword">return</span> <span class="hljs-comment">// ignore repeated calls</span>
    }
    g.closed = <span class="hljs-literal">true</span>
    <span class="hljs-comment">// free resources</span>
}
</code></pre>
<p>Alternatively, we can use <code>CompareAndSwap</code> on an atomic <code>Bool</code> instead of a mutex:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> Gate <span class="hljs-keyword">struct</span> {
    closed atomic.Bool
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(g *Gate)</span> <span class="hljs-title">Close</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">if</span> !g.closed.CompareAndSwap(<span class="hljs-literal">false</span>, <span class="hljs-literal">true</span>) {
        <span class="hljs-keyword">return</span> <span class="hljs-comment">// ignore repeated calls</span>
    }
    <span class="hljs-comment">// The gate is closed.</span>
    <span class="hljs-comment">// We can free resources now.</span>
}
</code></pre>
<p>The <code>Gate</code> type is now more compact and simple.</p>
<p>This isn't a very common use case — we usually want a goroutine to wait on a locked mutex and continue once it's unlocked. But for "early exit" situations, it's perfect.</p>
<hr />
<h2 id="heading-summary">Summary</h2>
<p>Atomics are a specialized but useful tool. You can use them for simple counters and flags, but be very careful when using them for more complex operations. You can also use them instead of mutexes to exit early.</p>
<p>Key points to remember:</p>
<ul>
<li><strong>Atomic operations</strong> translate to single CPU instructions and don't require locks.</li>
<li><strong>Composition of atomics</strong> is not atomic — combining multiple atomic operations doesn't make the whole operation atomic.</li>
<li><strong>Sequence-independent operations</strong> like <code>Add</code> can be safely composed, while sequence-dependent operations cannot.</li>
<li><strong>CompareAndSwap</strong> can replace mutexes in "early exit" scenarios.</li>
<li><strong>Use mutexes</strong> when you need to make composite operations atomic.</li>
</ul>
<p>Atomics provide a powerful way to create lock-free concurrent code, but they require careful understanding of their limitations and proper use.</p>
]]></content:encoded></item><item><title><![CDATA[Go: Using Context for Request Cancellation and Timeout Management]]></title><description><![CDATA[In programming, context refers to information about the environment in which an object exists or a function executes. In Go, context usually refers to the Context interface from the context package. It was originally designed to make working with HTT...]]></description><link>https://blog.fshtab.com/go-context-cancellation-timeouts</link><guid isPermaLink="true">https://blog.fshtab.com/go-context-cancellation-timeouts</guid><dc:creator><![CDATA[Fedor S]]></dc:creator><pubDate>Wed, 03 Jul 2024 08:20:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1766406533686/16d88083-8cea-46d8-95b3-252f5658cfd8.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In programming, <em>context</em> refers to information about the environment in which an object exists or a function executes. In Go, context usually refers to the <code>Context</code> interface from the <code>context</code> package. It was originally designed to make working with HTTP requests easier. However, contexts can also be used in regular concurrent code. Let's see how exactly.</p>
<hr />
<h2 id="heading-canceling-with-channel">Canceling with Channel</h2>
<p>Suppose we have an <code>execute()</code> function, which can run a given function and supports cancellation:</p>
<pre><code class="lang-go"><span class="hljs-comment">// execute runs fn in a separate goroutine</span>
<span class="hljs-comment">// and waits for the result unless canceled.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">execute</span><span class="hljs-params">(cancel &lt;-<span class="hljs-keyword">chan</span> <span class="hljs-keyword">struct</span>{}, fn <span class="hljs-keyword">func</span>()</span> <span class="hljs-title">int</span>) <span class="hljs-params">(<span class="hljs-keyword">int</span>, error)</span></span> {
    ch := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>, <span class="hljs-number">1</span>)

    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        ch &lt;- fn()
    }()

    <span class="hljs-keyword">select</span> {
    <span class="hljs-keyword">case</span> res := &lt;-ch:
        <span class="hljs-keyword">return</span> res, <span class="hljs-literal">nil</span>
    <span class="hljs-keyword">case</span> &lt;-cancel:
        <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>, errors.New(<span class="hljs-string">"canceled"</span>)
    }
}
</code></pre>
<p>Everything is familiar here:</p>
<ul>
<li>The function takes a channel through which it can receive a cancellation signal.</li>
<li>It runs <code>fn()</code> in a separate goroutine.</li>
<li>It uses select to wait for <code>fn()</code> to complete or cancel, whichever occurs first.</li>
</ul>
<p>Let's write a client that cancels operations with a 50% probability:</p>
<pre><code class="lang-go"><span class="hljs-comment">// work does something for 100 ms.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">work</span><span class="hljs-params">()</span> <span class="hljs-title">int</span></span> {
    time.Sleep(<span class="hljs-number">100</span> * time.Millisecond)
    fmt.Println(<span class="hljs-string">"work done"</span>)
    <span class="hljs-keyword">return</span> <span class="hljs-number">42</span>
}

<span class="hljs-comment">// maybeCancel waits for 50 ms and cancels with 50% probability.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">maybeCancel</span><span class="hljs-params">(cancel <span class="hljs-keyword">chan</span> <span class="hljs-keyword">struct</span>{})</span></span> {
    time.Sleep(<span class="hljs-number">50</span> * time.Millisecond)
    <span class="hljs-keyword">if</span> rand.Float32() &lt; <span class="hljs-number">0.5</span> {
        <span class="hljs-built_in">close</span>(cancel)
    }
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    cancel := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">struct</span>{})
    <span class="hljs-keyword">go</span> maybeCancel(cancel)

    res, err := execute(cancel, work)
    fmt.Println(res, err)
}
</code></pre>
<pre><code>work done
<span class="hljs-number">42</span> &lt;nil&gt;
</code></pre><p>Run it a few times:</p>
<pre><code><span class="hljs-number">0</span> canceled
</code></pre><pre><code>work done
<span class="hljs-number">42</span> &lt;nil&gt;
</code></pre><pre><code><span class="hljs-number">0</span> canceled
</code></pre><pre><code>work done
<span class="hljs-number">42</span> &lt;nil&gt;
</code></pre><p>No surprises here.</p>
<p>Now let's reimplement <code>execute</code> with context.</p>
<hr />
<h2 id="heading-canceling-with-context">Canceling with Context</h2>
<p>The main purpose of context in Go is to cancel operations.</p>
<p>Let's reimplement what we just did with a cancel channel – this time with a context. The <code>execute()</code> function accepts a context <code>ctx</code> instead of a <code>cancel</code> channel:</p>
<pre><code class="lang-go"><span class="hljs-comment">// execute runs fn in a separate goroutine</span>
<span class="hljs-comment">// and waits for the result unless canceled.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">execute</span><span class="hljs-params">(ctx context.Context, fn <span class="hljs-keyword">func</span>()</span> <span class="hljs-title">int</span>) <span class="hljs-params">(<span class="hljs-keyword">int</span>, error)</span></span> {
    ch := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>, <span class="hljs-number">1</span>)

    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        ch &lt;- fn()
    }()

    <span class="hljs-keyword">select</span> {
    <span class="hljs-keyword">case</span> res := &lt;-ch:
        <span class="hljs-keyword">return</span> res, <span class="hljs-literal">nil</span>
    <span class="hljs-keyword">case</span> &lt;-ctx.Done():       <span class="hljs-comment">// (1)</span>
        <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>, ctx.Err()  <span class="hljs-comment">// (2)</span>
    }
}
</code></pre>
<p>The code has barely changed:</p>
<ul>
<li>➊ Instead of the <code>cancel</code> channel, the cancellation signal comes from the <code>ctx.Done()</code> channel</li>
<li>➋ Instead of manually creating a "canceled" error, we return <code>ctx.Err()</code></li>
</ul>
<p>The client also changes slightly:</p>
<pre><code class="lang-go"><span class="hljs-comment">// maybeCancel waits for 50 ms and cancels with 50% probability.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">maybeCancel</span><span class="hljs-params">(cancel <span class="hljs-keyword">func</span>()</span>)</span> {
    time.Sleep(<span class="hljs-number">50</span> * time.Millisecond)
    <span class="hljs-keyword">if</span> rand.Float32() &lt; <span class="hljs-number">0.5</span> {
        cancel()
    }
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    ctx := context.Background()              <span class="hljs-comment">// (1)</span>
    ctx, cancel := context.WithCancel(ctx)   <span class="hljs-comment">// (2)</span>
    <span class="hljs-keyword">defer</span> cancel()                           <span class="hljs-comment">// (3)</span>

    <span class="hljs-keyword">go</span> maybeCancel(cancel)                   <span class="hljs-comment">// (4)</span>

    res, err := execute(ctx, work)           <span class="hljs-comment">// (5)</span>
    fmt.Println(res, err)
}
</code></pre>
<pre><code>work done
<span class="hljs-number">42</span> &lt;nil&gt;
</code></pre><p>Here's what we do:</p>
<ul>
<li>➊ Create an empty context with <code>context.Background()</code>.</li>
<li>➋ Create a manual cancel context based on the empty context with <code>context.WithCancel()</code>.</li>
<li>➌ Schedule a deferred cancel when <code>main()</code> exits.</li>
<li>➍ Cancel the context with a 50% probability.</li>
<li>➎ Pass the context to the <code>execute()</code> function.</li>
</ul>
<p><code>context.WithCancel()</code> returns the context itself and a <code>cancel</code> function to cancel it. Calling <code>cancel()</code> releases the resources occupied by the context and closes the <code>ctx.Done()</code> channel — we use this effect to interrupt <code>execute()</code>. If the context is canceled, <code>ctx.Err()</code> returns an error (in our case <code>context.Canceled</code>).</p>
<p>All in all, it works exactly the same as the previous version with the cancel channel:</p>
<pre><code>work done
<span class="hljs-number">42</span> &lt;nil&gt;
</code></pre><pre><code><span class="hljs-number">0</span> context canceled
</code></pre><pre><code><span class="hljs-number">0</span> context canceled
</code></pre><pre><code>work done
<span class="hljs-number">42</span> &lt;nil&gt;
</code></pre><p>A few nuances that were not present with the cancel channel:</p>
<p><strong>Context is layered</strong>. A context object is immutable. To add new properties to a context, a new (child) context is created based on the old (parent) context. That's why we first created an empty context and then a cancel context based on it:</p>
<pre><code class="lang-go"><span class="hljs-comment">// parent context</span>
ctx := context.Background()

<span class="hljs-comment">// child context</span>
ctx, cancel := context.WithCancel(ctx)
</code></pre>
<p>If the parent context is canceled, all child contexts are canceled as well (but not vice versa):</p>
<pre><code class="lang-go"><span class="hljs-comment">// parent context</span>
parentCtx, parentCancel := context.WithCancel(context.Background())

<span class="hljs-comment">// child context</span>
childCtx, childCancel := context.WithCancel(parentCtx)

<span class="hljs-comment">// parentCancel() cancels both parentCtx and childCtx.</span>
<span class="hljs-comment">// childCancel() cancels only childCtx.</span>
</code></pre>
<p><strong>Multiple cancels are safe</strong>. If you call <code>close()</code> on a channel twice, it will cause a panic. However, you can call <code>cancel()</code> on the context as many times as you want. The first cancel will work, and the rest will be ignored. This is convenient because you can schedule a deferred <code>cancel()</code> right after creating the context, and explicitly cancel the context if necessary (as we did in the <code>maybeCancel</code> function). This wouldn't be possible with a channel.</p>
<hr />
<h2 id="heading-timeout">Timeout</h2>
<p>The real power of context is its ability to handle both manual cancellation and <em>timeouts</em>.</p>
<pre><code class="lang-go"><span class="hljs-comment">// execute runs fn in a separate goroutine</span>
<span class="hljs-comment">// and waits for the result unless canceled.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">execute</span><span class="hljs-params">(ctx context.Context, fn <span class="hljs-keyword">func</span>()</span> <span class="hljs-title">int</span>) <span class="hljs-params">(<span class="hljs-keyword">int</span>, error)</span></span> {
    <span class="hljs-comment">// remains unchanged</span>
}

<span class="hljs-comment">// work does something for 100 ms.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">work</span><span class="hljs-params">()</span> <span class="hljs-title">int</span></span> {
    <span class="hljs-comment">// remains unchanged</span>
}
</code></pre>
<p>With a timeout of 150 ms, <code>work()</code> completes on time:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    timeout := <span class="hljs-number">150</span> * time.Millisecond
    ctx, cancel := context.WithTimeout(context.Background(), timeout)  <span class="hljs-comment">// (1)</span>
    <span class="hljs-keyword">defer</span> cancel()

    res, err := execute(ctx, work)
    fmt.Println(res, err)
}
</code></pre>
<pre><code>work done
<span class="hljs-number">42</span> &lt;nil&gt;
</code></pre><p>With a timeout of 50 ms, execution gets canceled:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    timeout := <span class="hljs-number">50</span> * time.Millisecond
    ctx, cancel := context.WithTimeout(context.Background(), timeout)  <span class="hljs-comment">// (1)</span>
    <span class="hljs-keyword">defer</span> cancel()

    res, err := execute(ctx, work)
    fmt.Println(res, err)
}
</code></pre>
<pre><code><span class="hljs-number">0</span> context deadline exceeded
</code></pre><p>The <code>execute()</code> function remains unchanged, but <code>context.WithCancel()</code> in main is now replaced with <code>context.WithTimeout()</code> ➊. This change causes <code>execute()</code> to fail with a timeout error (<code>context.DeadlineExceeded</code>) when <code>work()</code> doesn't finish on time.</p>
<p>Thanks to the context, the <code>execute()</code> function doesn't need to know whether the cancellation was triggered manually or by a timeout. All it needs to do is listen for the cancellation signal on the <code>ctx.Done()</code> channel.</p>
<p>Convenient!</p>
<hr />
<h2 id="heading-parent-and-child-timeouts">Parent and Child Timeouts</h2>
<p>Let's say we have the same <code>execute()</code> function and two functions it can run — the faster <code>work()</code> and the slower <code>slow()</code>:</p>
<pre><code class="lang-go"><span class="hljs-comment">// work does something for 100 ms.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">work</span><span class="hljs-params">()</span> <span class="hljs-title">int</span></span> {
    time.Sleep(<span class="hljs-number">100</span> * time.Millisecond)
    <span class="hljs-keyword">return</span> <span class="hljs-number">42</span>
}

<span class="hljs-comment">// slow does something for 200 ms.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">slow</span><span class="hljs-params">()</span> <span class="hljs-title">int</span></span> {
    time.Sleep(<span class="hljs-number">200</span> * time.Millisecond)
    <span class="hljs-keyword">return</span> <span class="hljs-number">99</span>
}
</code></pre>
<p>We want to run both functions, but we don't want to wait more than 150 ms total. We can create a parent context with a 150 ms timeout and then create child contexts for each function:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    parentCtx, parentCancel := context.WithTimeout(context.Background(), <span class="hljs-number">150</span>*time.Millisecond)
    <span class="hljs-keyword">defer</span> parentCancel()

    <span class="hljs-comment">// Child context for work()</span>
    workCtx, workCancel := context.WithCancel(parentCtx)
    <span class="hljs-keyword">defer</span> workCancel()

    <span class="hljs-comment">// Child context for slow()</span>
    slowCtx, slowCancel := context.WithCancel(parentCtx)
    <span class="hljs-keyword">defer</span> slowCancel()

    <span class="hljs-keyword">var</span> wg sync.WaitGroup
    wg.Add(<span class="hljs-number">2</span>)

    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">defer</span> wg.Done()
        res, err := execute(workCtx, work)
        fmt.Println(<span class="hljs-string">"work:"</span>, res, err)
    }()

    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">defer</span> wg.Done()
        res, err := execute(slowCtx, slow)
        fmt.Println(<span class="hljs-string">"slow:"</span>, res, err)
    }()

    wg.Wait()
}
</code></pre>
<pre><code>work: <span class="hljs-number">42</span> &lt;nil&gt;
slow: <span class="hljs-number">0</span> context deadline exceeded
</code></pre><p>Here, <code>work()</code> completes successfully, but <code>slow()</code> gets canceled because it exceeds the parent timeout of 150 ms.</p>
<hr />
<h2 id="heading-deadline">Deadline</h2>
<p>Instead of specifying a timeout duration, you can set an absolute deadline:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    deadline := time.Now().Add(<span class="hljs-number">150</span> * time.Millisecond)
    ctx, cancel := context.WithDeadline(context.Background(), deadline)
    <span class="hljs-keyword">defer</span> cancel()

    res, err := execute(ctx, work)
    fmt.Println(res, err)
}
</code></pre>
<pre><code>work done
<span class="hljs-number">42</span> &lt;nil&gt;
</code></pre><p><code>context.WithDeadline()</code> works similarly to <code>context.WithTimeout()</code>, but takes an absolute time instead of a duration.</p>
<hr />
<h2 id="heading-cancellation-cause">Cancellation Cause</h2>
<p>In Go 1.21+, you can specify a custom cause for cancellation. The <code>context.WithCancelCause()</code> function takes an additional parameter: the root <em>cause</em> of the cancellation.</p>
<pre><code class="lang-go">ctx, cancel := context.WithCancelCause(context.Background())
cancel(errors.New(<span class="hljs-string">"the night is dark"</span>))
</code></pre>
<p>Use <code>context.Cause()</code> to get the error's cause:</p>
<pre><code class="lang-go">fmt.Println(ctx.Err())
<span class="hljs-comment">// context canceled</span>

fmt.Println(context.Cause(ctx))
<span class="hljs-comment">// the night is dark</span>
</code></pre>
<pre><code>context canceled
the night is dark
</code></pre><p>In Go 1.21+, you can specify a custom cause for timeout (or deadline) cancellation when creating a context. This cause is accessible through <code>context.Cause()</code> when the context is canceled due to a timeout (or deadline):</p>
<pre><code class="lang-go">cause := errors.New(<span class="hljs-string">"the night is dark"</span>)
ctx, cancel := context.WithTimeoutCause(
    context.Background(), <span class="hljs-number">10</span>*time.Millisecond, cause,
)
<span class="hljs-keyword">defer</span> cancel()

time.Sleep(<span class="hljs-number">50</span> * time.Millisecond)
fmt.Println(ctx.Err())
<span class="hljs-comment">// context deadline exceeded</span>
fmt.Println(context.Cause(ctx))
<span class="hljs-comment">// the night is dark</span>
</code></pre>
<pre><code>context deadline exceeded
the night is dark
</code></pre><hr />
<h2 id="heading-contextafterfunc">context.AfterFunc</h2>
<p>Suppose we're performing a long-running task with the option to cancel:</p>
<pre><code class="lang-go"><span class="hljs-comment">// work does something for 100 ms.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">work</span><span class="hljs-params">(ctx context.Context)</span></span> {
    <span class="hljs-keyword">select</span> {
    <span class="hljs-keyword">case</span> &lt;-time.After(<span class="hljs-number">100</span> * time.Millisecond):
    <span class="hljs-keyword">case</span> &lt;-ctx.Done():
    }
}
</code></pre>
<p>Let's set the timeout to 50 ms (expecting <code>work()</code> to be canceled):</p>
<pre><code class="lang-go"><span class="hljs-comment">// the context is canceled after a 50 ms timeout</span>
ctx, cancel := context.WithTimeout(context.Background(), <span class="hljs-number">50</span>*time.Millisecond)
<span class="hljs-keyword">defer</span> cancel()

start := time.Now()
work(ctx)

<span class="hljs-keyword">if</span> ctx.Err() != <span class="hljs-literal">nil</span> {
    fmt.Println(<span class="hljs-string">"canceled after"</span>, time.Since(start))
}
</code></pre>
<pre><code>canceled after <span class="hljs-number">50</span>ms
</code></pre><p>What should we do if <code>work()</code> has occupied resources that need to be freed upon cancellation?</p>
<pre><code class="lang-go"><span class="hljs-comment">// cleanup frees up occupied resources.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">cleanup</span><span class="hljs-params">()</span></span> {
    fmt.Println(<span class="hljs-string">"cleanup"</span>)
}
</code></pre>
<p>We can add the <code>cleanup()</code> call directly into <code>work()</code>:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">work</span><span class="hljs-params">(ctx context.Context)</span></span> {
    <span class="hljs-keyword">select</span> {
    <span class="hljs-keyword">case</span> &lt;-time.After(<span class="hljs-number">100</span> * time.Millisecond):
    <span class="hljs-keyword">case</span> &lt;-ctx.Done():
        cleanup()
    }
}
</code></pre>
<p>But Go 1.21+ offers a more flexible solution.</p>
<h3 id="heading-calling-a-function-on-context-cancellation">Calling a Function on Context Cancellation</h3>
<p>You can register a function to execute when the context is canceled:</p>
<pre><code class="lang-go"><span class="hljs-comment">// the context is canceled after a 50 ms timeout</span>
ctx, cancel := context.WithTimeout(context.Background(), <span class="hljs-number">50</span>*time.Millisecond)
<span class="hljs-keyword">defer</span> cancel()

<span class="hljs-comment">// cleanup is called after the context is canceled</span>
context.AfterFunc(ctx, cleanup)

start := time.Now()
work(ctx)

<span class="hljs-keyword">if</span> ctx.Err() != <span class="hljs-literal">nil</span> {
    fmt.Println(<span class="hljs-string">"canceled after"</span>, time.Since(start))
}
</code></pre>
<pre><code>cleanup
canceled after <span class="hljs-number">50</span>ms
</code></pre><p>In this version, <code>work()</code> doesn't need to know about <code>cleanup()</code>.</p>
<p><code>context.AfterFunc()</code> offers the following:</p>
<ul>
<li><code>cleanup()</code> runs in a separate goroutine.</li>
<li>You can register multiple functions by calling <code>AfterFunc()</code> multiple times. Each function runs independently in a separate goroutine when the context is canceled.</li>
<li>You can change your mind and "detach" a registered function.</li>
</ul>
<h3 id="heading-registering-a-function-after-the-context-is-canceled">Registering a Function After the Context is Canceled</h3>
<p>If the context is already canceled when the function is registered, the function executes immediately:</p>
<pre><code class="lang-go"><span class="hljs-comment">// the context is canceled after a 50 ms timeout</span>
ctx, cancel := context.WithTimeout(context.Background(), <span class="hljs-number">50</span>*time.Millisecond)
<span class="hljs-keyword">defer</span> cancel()

start := time.Now()
work(ctx)

<span class="hljs-keyword">if</span> ctx.Err() != <span class="hljs-literal">nil</span> {
    fmt.Println(<span class="hljs-string">"canceled after"</span>, time.Since(start))
}

<span class="hljs-comment">// cleanup is called immediately since the context is already canceled</span>
context.AfterFunc(ctx, cleanup)
</code></pre>
<pre><code>canceled after <span class="hljs-number">50</span>ms
cleanup
</code></pre><h3 id="heading-canceling-the-registration">Canceling the Registration</h3>
<p>Example of "changing one's mind":</p>
<pre><code class="lang-go"><span class="hljs-comment">// the context is canceled after a 50 ms timeout</span>
ctx, cancel := context.WithTimeout(context.Background(), <span class="hljs-number">50</span>*time.Millisecond)
<span class="hljs-keyword">defer</span> cancel()

<span class="hljs-comment">// cleanup is called after the context is canceled</span>
stopCleanup := context.AfterFunc(ctx, cleanup)  <span class="hljs-comment">// (1)</span>

<span class="hljs-comment">// I changed my mind, let's not call cleanup</span>
stopped := stopCleanup()                        <span class="hljs-comment">// (2)</span>
work(ctx)

fmt.Println(<span class="hljs-string">"stopped cleanup:"</span>, stopped)
</code></pre>
<pre><code>stopped cleanup: <span class="hljs-literal">true</span>
</code></pre><p>In ➊, we saved the "detach" function (returned by <code>context.AfterFunc()</code>) in the <code>stopCleanup</code> variable, and in ➋, we called it, detaching <code>cleanup()</code> from <code>ctx</code>. As a result, the context was canceled due to the timeout, but <code>cleanup()</code> did not execute.</p>
<p>If the context is canceled and the function has already started executing, you can't detach it:</p>
<pre><code class="lang-go"><span class="hljs-comment">// the context is canceled after a 50 ms timeout</span>
ctx, cancel := context.WithTimeout(context.Background(), <span class="hljs-number">50</span>*time.Millisecond)
<span class="hljs-keyword">defer</span> cancel()

<span class="hljs-comment">// cleanup is called after the context is canceled</span>
stopCleanup := context.AfterFunc(ctx, cleanup)

work(ctx)

<span class="hljs-comment">// I changed my mind about calling cleanup, but it's already too late</span>
stopped := stopCleanup()
fmt.Println(<span class="hljs-string">"stopped cleanup:"</span>, stopped)
</code></pre>
<pre><code>cleanup
stopped cleanup: <span class="hljs-literal">false</span>
</code></pre><p>Phew. <code>AfterFunc()</code> is not the most intuitive context-related feature.</p>
<hr />
<h2 id="heading-context-with-values">Context with Values</h2>
<p>The main purpose of context in Go is to cancel operations, either manually or by timeout/deadline. But it can also pass additional information about a call using <code>context.WithValue()</code>, which creates a context with a value for a specific key:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> contextKey <span class="hljs-keyword">string</span>

<span class="hljs-comment">// "id" and "user" keys</span>
<span class="hljs-keyword">var</span> idKey = contextKey(<span class="hljs-string">"id"</span>)
<span class="hljs-keyword">var</span> userKey = contextKey(<span class="hljs-string">"user"</span>)

<span class="hljs-comment">// work does something.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">work</span><span class="hljs-params">()</span> <span class="hljs-title">int</span></span> {
    <span class="hljs-keyword">return</span> <span class="hljs-number">42</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    {
        ctx := context.Background()
        <span class="hljs-comment">// context with request ID</span>
        ctx = context.WithValue(ctx, idKey, <span class="hljs-number">1234</span>)
        <span class="hljs-comment">// and user</span>
        ctx = context.WithValue(ctx, userKey, <span class="hljs-string">"admin"</span>)
        res := execute(ctx, work)
        fmt.Println(res)
    }

    {
        <span class="hljs-comment">// empty context</span>
        ctx := context.Background()
        res := execute(ctx, work)
        fmt.Println(res)
    }
}
</code></pre>
<p>We use values of a custom type <code>contextKey</code> as keys instead of strings or numbers. This prevents conflicts if two packages modify the same context and both add a value with the <code>id</code> or <code>user</code> key.</p>
<p>To retrieve a value by key, use the <code>Value()</code> method:</p>
<pre><code class="lang-go"><span class="hljs-comment">// execute runs fn with respect to ctx.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">execute</span><span class="hljs-params">(ctx context.Context, fn <span class="hljs-keyword">func</span>()</span> <span class="hljs-title">int</span>) <span class="hljs-title">int</span></span> {
    reqId := ctx.Value(idKey)
    <span class="hljs-keyword">if</span> reqId != <span class="hljs-literal">nil</span> {
        fmt.Printf(<span class="hljs-string">"Request ID = %d\n"</span>, reqId)
    } <span class="hljs-keyword">else</span> {
        fmt.Println(<span class="hljs-string">"Request ID unknown"</span>)
    }

    user := ctx.Value(userKey)
    <span class="hljs-keyword">if</span> user != <span class="hljs-literal">nil</span> {
        fmt.Printf(<span class="hljs-string">"Request user = %s\n"</span>, user)
    } <span class="hljs-keyword">else</span> {
        fmt.Println(<span class="hljs-string">"Request user unknown"</span>)
    }
    <span class="hljs-keyword">return</span> fn()
}
</code></pre>
<pre><code>Request ID = <span class="hljs-number">1234</span>
Request user = admin
<span class="hljs-number">42</span>
Request ID unknown
Request user unknown
<span class="hljs-number">42</span>
</code></pre><p>Both <code>context.WithValue()</code> and <code>Context.Value()</code> work with values of type <code>any</code> (they were added to the standard library long before generics):</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">WithValue</span><span class="hljs-params">(parent Context, key, val any)</span> <span class="hljs-title">Context</span></span>

<span class="hljs-keyword">type</span> Context <span class="hljs-keyword">interface</span> {
    <span class="hljs-comment">// ...</span>
    Value(key any) any
}
</code></pre>
<p>I'm mentioning this feature for completeness, but it's generally better to avoid passing values in context. It's better to use explicit parameters or custom structs instead.</p>
<hr />
<h2 id="heading-summary">Summary</h2>
<p>Use context to safely cancel and timeout operations in a concurrent environment. It's a perfect fit for remote calls, pipelines, or other long-running operations.</p>
<p>Now you know how to:</p>
<ul>
<li>Manually cancel operations.</li>
<li>Cancel on timeout or deadline.</li>
<li>Use child contexts to restrict timeouts.</li>
<li>Specify the cancellation reason.</li>
<li>Register functions to execute when the context is canceled.</li>
</ul>
<p>Context provides a powerful and standardized way to manage cancellation and timeouts across your concurrent Go programs.</p>
]]></content:encoded></item><item><title><![CDATA[Go: Using Semaphores to Manage Concurrent Execution]]></title><description><![CDATA[Having the full power of multi-core hardware is great, but sometimes we prefer to limit concurrency in certain parts of a system. Semaphores are a great way to do this. Let's learn more about them!

Mutex: One Goroutine at a Time
Let's say our progra...]]></description><link>https://blog.fshtab.com/go-semaphores-concurrency-control</link><guid isPermaLink="true">https://blog.fshtab.com/go-semaphores-concurrency-control</guid><dc:creator><![CDATA[Fedor S]]></dc:creator><pubDate>Wed, 12 Jun 2024 12:18:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1766406508990/11f00422-4dd5-4907-9bfd-7cce8de32813.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Having the full power of multi-core hardware is great, but sometimes we prefer to limit concurrency in certain parts of a system. Semaphores are a great way to do this. Let's learn more about them!</p>
<hr />
<h2 id="heading-mutex-one-goroutine-at-a-time">Mutex: One Goroutine at a Time</h2>
<p>Let's say our program needs to call a legacy system, represented by the <code>External</code> type. This system is so ancient that it can handle no more than one call at a time. That's why we protect it with a mutex:</p>
<pre><code class="lang-go"><span class="hljs-comment">// External is a adapter for an external system.</span>
<span class="hljs-keyword">type</span> External <span class="hljs-keyword">struct</span> {
    lock sync.Mutex
}

<span class="hljs-comment">// Call calls the external system.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(e *External)</span> <span class="hljs-title">Call</span><span class="hljs-params">()</span></span> {
    e.lock.Lock()
    <span class="hljs-keyword">defer</span> e.lock.Unlock()
    <span class="hljs-comment">// Simulate a remote call.</span>
    time.Sleep(<span class="hljs-number">10</span> * time.Millisecond)
}
</code></pre>
<p>Now, no matter how many goroutines try to access the external system at the same time, they'll have to take turns:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">const</span> nCalls = <span class="hljs-number">12</span>
    ex := <span class="hljs-built_in">new</span>(External)
    start := time.Now()

    <span class="hljs-keyword">var</span> wg sync.WaitGroup
    <span class="hljs-keyword">for</span> <span class="hljs-keyword">range</span> nCalls {
        wg.Go(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
            ex.Call()
            fmt.Print(<span class="hljs-string">"."</span>)
        })
    }
    wg.Wait()

    fmt.Printf(
        <span class="hljs-string">"\n%d calls took %d ms\n"</span>,
        nCalls, time.Since(start).Milliseconds(),
    )
}
</code></pre>
<pre><code>............
<span class="hljs-number">12</span> calls took <span class="hljs-number">120</span> ms
</code></pre><p>Suppose the developers of the legacy system made some changes and now they say we can make up to four simultaneous calls. In this case, our approach with the mutex stops working, because it blocks all goroutines except the one that managed to lock the mutex.</p>
<p>It would be great to use a tool that allows several goroutines to run at the same time, but no more than N. Luckily for us, such a tool already exists.</p>
<hr />
<h2 id="heading-semaphore-n-goroutines-at-a-time">Semaphore: ≤ N Goroutines at a Time</h2>
<p>So, we want to make sure that no more than 4 goroutines access the external system at the same time. To do this, we'll use a <em>semaphore</em>. You can think of a semaphore as a container with N available slots and two operations: <em>acquire</em> to take a slot and <em>release</em> to free a slot.</p>
<p>Here are the semaphore rules:</p>
<ul>
<li>Calling <code>Acquire</code> takes a free slot.</li>
<li>If there are no free slots, <code>Acquire</code> blocks the goroutine that called it.</li>
<li>Calling <code>Release</code> frees up a previously taken slot.</li>
<li>If there are any goroutines blocked on <code>Acquire</code> when <code>Release</code> is called, one of them will immediately take the freed slot and unblock.</li>
</ul>
<p>Let's see how this works. To keep things simple, let's assume that someone has already implemented a <code>Semaphore</code> type for us, and we can just use it.</p>
<p>We add a semaphore to the external system adapter:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> External <span class="hljs-keyword">struct</span> {
    sema Semaphore
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">NewExternal</span><span class="hljs-params">(maxConc <span class="hljs-keyword">int</span>)</span> *<span class="hljs-title">External</span></span> {
    <span class="hljs-comment">// maxConc sets the maximum allowed</span>
    <span class="hljs-comment">// number of concurrent calls.</span>
    <span class="hljs-keyword">return</span> &amp;External{NewSemaphore(maxConc)}
}
</code></pre>
<p>We acquire a spot in the semaphore before calling the external system. After the call, we release it:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(e *External)</span> <span class="hljs-title">Call</span><span class="hljs-params">()</span></span> {
    e.sema.Acquire()
    <span class="hljs-keyword">defer</span> e.sema.Release()
    <span class="hljs-comment">// Simulate a remote call.</span>
    time.Sleep(<span class="hljs-number">10</span> * time.Millisecond)
}
</code></pre>
<p>Now let's allow 4 concurrent calls and perform a total of 12 calls. The client code doesn't change:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">const</span> nCalls = <span class="hljs-number">12</span>
    <span class="hljs-keyword">const</span> nConc = <span class="hljs-number">4</span>

    ex := NewExternal(nConc)
    start := time.Now()

    <span class="hljs-keyword">var</span> wg sync.WaitGroup
    <span class="hljs-keyword">for</span> <span class="hljs-keyword">range</span> nCalls {
        wg.Go(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
            ex.Call()
            fmt.Print(<span class="hljs-string">"."</span>)
        })
    }
    wg.Wait()

    fmt.Printf(
        <span class="hljs-string">"\n%d calls took %d ms\n"</span>,
        nCalls, time.Since(start).Milliseconds(),
    )
}
</code></pre>
<pre><code>............
<span class="hljs-number">12</span> calls took <span class="hljs-number">30</span> ms
</code></pre><p>12 calls were completed in three steps (each step = 4 concurrent calls). Each step took 10 ms, so the total time was 30 ms.</p>
<p>You might have noticed a downside to this approach: even though only 4 goroutines (<code>nConc</code>) run concurrently, we actually start all 12 (<code>nCalls</code>) right away. With small numbers, this isn't a big deal, but if <code>nCalls</code> is large, the waiting goroutines will use up memory for no good reason.</p>
<p>We can modify the program so that there are never more than <code>nConc</code> goroutines at any given time. To do this, we add the <code>Acquire</code> and <code>Release</code> methods directly to <code>External</code> and remove them from <code>Call</code>:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> External <span class="hljs-keyword">struct</span> {
    <span class="hljs-comment">// Semaphore is embedded into External so clients</span>
    <span class="hljs-comment">// can call Acquire and Release directly on External.</span>
    Semaphore
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">NewExternal</span><span class="hljs-params">(maxConc <span class="hljs-keyword">int</span>)</span> *<span class="hljs-title">External</span></span> {
    <span class="hljs-keyword">return</span> &amp;External{NewSemaphore(maxConc)}
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(e *External)</span> <span class="hljs-title">Call</span><span class="hljs-params">()</span></span> {
    <span class="hljs-comment">// Simulate a remote call.</span>
    time.Sleep(<span class="hljs-number">10</span> * time.Millisecond)
}
</code></pre>
<p>The client calls <code>Acquire</code> before starting each new goroutine in the loop, and calls <code>Release</code> when it's finished:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">const</span> nCalls = <span class="hljs-number">12</span>
    <span class="hljs-keyword">const</span> nConc = <span class="hljs-number">4</span>

    ex := NewExternal(nConc)
    start := time.Now()

    <span class="hljs-keyword">var</span> wg sync.WaitGroup
    <span class="hljs-keyword">for</span> <span class="hljs-keyword">range</span> nCalls {
        ex.Acquire()
        wg.Go(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
            <span class="hljs-keyword">defer</span> ex.Release()
            ex.Call()
            fmt.Print(<span class="hljs-string">"."</span>)
        })
    }
    wg.Wait()

    fmt.Printf(
        <span class="hljs-string">"\n%d calls took %d ms\n"</span>,
        nCalls, time.Since(start).Milliseconds(),
    )
}
</code></pre>
<pre><code>............
<span class="hljs-number">12</span> calls took <span class="hljs-number">30</span> ms
</code></pre><p>Now there are never more than 4 goroutines at any time (not counting the main goroutine, of course).</p>
<p>In summary, the semaphore helped us solve the problem of limited concurrency:</p>
<ul>
<li>goroutines are allowed to run concurrently,</li>
<li>but no more than N at the same time.</li>
</ul>
<p>Unfortunately, the standard library doesn't include a <code>Semaphore</code> type. So in the next step, we'll implement it ourselves!</p>
<blockquote>
<p>There is a semaphore available in the <code>golang.org/x/sync/semaphore</code> package. But for simple cases like ours, it's perfectly fine to use your own implementation.</p>
</blockquote>
<hr />
<h2 id="heading-implementing-a-semaphore">Implementing a Semaphore</h2>
<p>Here's a simple implementation of a semaphore using a channel:</p>
<pre><code class="lang-go"><span class="hljs-comment">// A synchronization semaphore.</span>
<span class="hljs-keyword">type</span> Semaphore <span class="hljs-keyword">struct</span> {
    ch <span class="hljs-keyword">chan</span> <span class="hljs-keyword">struct</span>{}
}

<span class="hljs-comment">// NewSemaphore creates a new semaphore with the given capacity.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">NewSemaphore</span><span class="hljs-params">(n <span class="hljs-keyword">int</span>)</span> *<span class="hljs-title">Semaphore</span></span> {
    s := &amp;Semaphore{
        ch: <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">struct</span>{}, n),
    }
    <span class="hljs-comment">// Fill the channel with tokens.</span>
    <span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i &lt; n; i++ {
        s.ch &lt;- <span class="hljs-keyword">struct</span>{}{}
    }
    <span class="hljs-keyword">return</span> s
}

<span class="hljs-comment">// Acquire takes a spot in the semaphore if one is available.</span>
<span class="hljs-comment">// Otherwise, it blocks the calling goroutine.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(s *Semaphore)</span> <span class="hljs-title">Acquire</span><span class="hljs-params">()</span></span> {
    &lt;-s.ch
}

<span class="hljs-comment">// Release frees up a spot in the semaphore and unblocks</span>
<span class="hljs-comment">// one of the blocked goroutines (if there are any).</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(s *Semaphore)</span> <span class="hljs-title">Release</span><span class="hljs-params">()</span></span> {
    s.ch &lt;- <span class="hljs-keyword">struct</span>{}{}
}
</code></pre>
<p>The implementation uses a buffered channel with capacity N. Initially, the channel is filled with N tokens (empty structs). When a goroutine calls <code>Acquire()</code>, it tries to receive a token from the channel. If tokens are available, it gets one immediately. If not, it blocks until a token becomes available. When a goroutine calls <code>Release()</code>, it sends a token back to the channel, which unblocks one waiting goroutine.</p>
<p>This implementation is simple, safe, and efficient. It avoids data races and busy-waiting, making it suitable for production use.</p>
<hr />
<h2 id="heading-rendezvous">Rendezvous</h2>
<p>Sometimes you need two goroutines to wait for each other at a specific point before continuing. This pattern is called a <em>rendezvous</em>.</p>
<p>Let's say we have two goroutines that need to synchronize at a certain point. In the first step, each goroutine wants to wait for the other. Here's what their execution looks like without a rendezvous:</p>
<pre><code class="lang-go"><span class="hljs-keyword">var</span> wg sync.WaitGroup

wg.Go(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
    fmt.Println(<span class="hljs-string">"1: started"</span>)
    time.Sleep(<span class="hljs-number">10</span> * time.Millisecond)
    fmt.Println(<span class="hljs-string">"1: reached the sync point"</span>)

    <span class="hljs-comment">// Sync point: how do I wait for the second goroutine?</span>

    fmt.Println(<span class="hljs-string">"1: going further"</span>)
    time.Sleep(<span class="hljs-number">20</span> * time.Millisecond)
    fmt.Println(<span class="hljs-string">"1: done"</span>)
})

time.Sleep(<span class="hljs-number">20</span> * time.Millisecond)

wg.Go(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
    fmt.Println(<span class="hljs-string">"2: started"</span>)
    time.Sleep(<span class="hljs-number">20</span> * time.Millisecond)
    fmt.Println(<span class="hljs-string">"2: reached the sync point"</span>)

    <span class="hljs-comment">// Sync point: how do I wait for the first goroutine?</span>

    fmt.Println(<span class="hljs-string">"2: going further"</span>)
    time.Sleep(<span class="hljs-number">10</span> * time.Millisecond)
    fmt.Println(<span class="hljs-string">"2: done"</span>)
})

wg.Wait()
</code></pre>
<pre><code><span class="hljs-number">1</span>: started
<span class="hljs-number">1</span>: reached the sync point
<span class="hljs-number">1</span>: going further
<span class="hljs-number">2</span>: started
<span class="hljs-number">1</span>: done
<span class="hljs-number">2</span>: reached the sync point
<span class="hljs-number">2</span>: going further
<span class="hljs-number">2</span>: done
</code></pre><p>As you can see, the second goroutine is just getting started, while the first one is already finished. No one is waiting for anyone else.</p>
<p>Let's set up a rendezvous for them:</p>
<pre><code class="lang-go"><span class="hljs-keyword">var</span> wg sync.WaitGroup

ready1 := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">struct</span>{})
ready2 := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">struct</span>{})

wg.Go(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
    fmt.Println(<span class="hljs-string">"1: started"</span>)
    time.Sleep(<span class="hljs-number">10</span> * time.Millisecond)
    fmt.Println(<span class="hljs-string">"1: reached the sync point"</span>)

    <span class="hljs-comment">// Sync point.</span>
    <span class="hljs-built_in">close</span>(ready1)
    &lt;-ready2

    fmt.Println(<span class="hljs-string">"1: going further"</span>)
    time.Sleep(<span class="hljs-number">20</span> * time.Millisecond)
    fmt.Println(<span class="hljs-string">"1: done"</span>)
})

time.Sleep(<span class="hljs-number">20</span> * time.Millisecond)

wg.Go(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
    fmt.Println(<span class="hljs-string">"2: started"</span>)
    time.Sleep(<span class="hljs-number">20</span> * time.Millisecond)
    fmt.Println(<span class="hljs-string">"2: reached the sync point"</span>)

    <span class="hljs-comment">// Sync point.</span>
    <span class="hljs-built_in">close</span>(ready2)
    &lt;-ready1

    fmt.Println(<span class="hljs-string">"2: going further"</span>)
    time.Sleep(<span class="hljs-number">10</span> * time.Millisecond)
    fmt.Println(<span class="hljs-string">"2: done"</span>)
})

wg.Wait()
</code></pre>
<pre><code><span class="hljs-number">1</span>: started
<span class="hljs-number">1</span>: reached the sync point
<span class="hljs-number">2</span>: started
<span class="hljs-number">2</span>: reached the sync point
<span class="hljs-number">2</span>: going further
<span class="hljs-number">1</span>: going further
<span class="hljs-number">2</span>: done
<span class="hljs-number">1</span>: done
</code></pre><p>Now everything works fine: the goroutines waited for each other at the sync point before moving on.</p>
<p>Here's how it works:</p>
<ul>
<li>G1 closes its own channel when it's ready and then blocks on the other channel. G2 does the same thing.</li>
<li>When G1's channel is closed, it unblocks G2, and when G2's channel is closed, it unblocks G1.</li>
<li>As a result, both goroutines are unblocked at the same time.</li>
</ul>
<p>Here, we close the channel to signal an event. We've done this before:</p>
<ul>
<li>With a done channel in the Channels chapter (the goroutine signals the caller that the work is finished).</li>
<li>With a cancel channel in the Pipelines chapter (the caller signals the goroutine to stop working).</li>
</ul>
<p><strong>Caution: Using Print for debugging</strong></p>
<p>Since printing uses a single output device (stdout), goroutines that print concurrently have to synchronize access to it. So, using print statements adds a synchronization point to your program. This can cause unexpected results that are different from what actually happens in production (where there is no printing).</p>
<p>In the book, I use print statements only because it's much harder to understand the material without them.</p>
<hr />
<h2 id="heading-synchronization-barrier">Synchronization Barrier</h2>
<p>Imagine you walk up to a crosswalk with a traffic light and see a button that's supposed to turn the light green. You press the button, but nothing happens. Another person comes up behind you and presses the button too, but still nothing changes. Two more people arrive, both press the button, but the light stays red. Now the four of you are just standing there, not sure what to do. Finally, a fifth person comes, presses the button, and the light turns green. All five of you cross the street together.</p>
<p>This kind of logic in concurrent programs is called a <em>synchronization barrier</em>:</p>
<ul>
<li>The barrier has a counter (starting at 0) and a threshold N.</li>
<li>Each goroutine that reaches the barrier increases the counter by 1.</li>
<li>The barrier blocks any goroutine that reaches it.</li>
<li>Once the counter reaches N, the barrier unblocks all waiting goroutines.</li>
</ul>
<p>Let's say there are N goroutines. Each one first does a preparation step, then the main step. Here's what their execution looks like without a barrier:</p>
<pre><code class="lang-go"><span class="hljs-keyword">const</span> nWorkers = <span class="hljs-number">4</span>
start := time.Now()

<span class="hljs-keyword">var</span> wg sync.WaitGroup
<span class="hljs-keyword">for</span> i := <span class="hljs-keyword">range</span> nWorkers {
    wg.Go(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-comment">// Simulate the preparation step.</span>
        dur := time.Duration((i+<span class="hljs-number">1</span>)*<span class="hljs-number">10</span>) * time.Millisecond
        time.Sleep(dur)
        fmt.Printf(<span class="hljs-string">"ready to go after %d ms\n"</span>, dur.Milliseconds())

        <span class="hljs-comment">// Simulate the main step.</span>
        fmt.Println(<span class="hljs-string">"go!"</span>)
    })
}

wg.Wait()
fmt.Printf(<span class="hljs-string">"all done in %d ms\n"</span>, time.Since(start).Milliseconds())
</code></pre>
<pre><code>ready to go after <span class="hljs-number">10</span> ms
go!
ready to go after <span class="hljs-number">20</span> ms
go!
ready to go after <span class="hljs-number">30</span> ms
go!
ready to go after <span class="hljs-number">40</span> ms
go!
all done <span class="hljs-keyword">in</span> <span class="hljs-number">40</span> ms
</code></pre><p>Each goroutine proceeds to the main step as soon as it's ready, without waiting for the others.</p>
<p>Let's say we want the goroutines to wait for each other before moving on to the main step. To do this, we just need to add a barrier after the preparation step:</p>
<pre><code class="lang-go"><span class="hljs-keyword">const</span> nWorkers = <span class="hljs-number">4</span>
start := time.Now()

<span class="hljs-keyword">var</span> wg sync.WaitGroup
b := NewBarrier(nWorkers)
<span class="hljs-keyword">for</span> i := <span class="hljs-keyword">range</span> nWorkers {
    wg.Go(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-comment">// Simulate the preparation step.</span>
        dur := time.Duration((i+<span class="hljs-number">1</span>)*<span class="hljs-number">10</span>) * time.Millisecond
        time.Sleep(dur)
        fmt.Printf(<span class="hljs-string">"ready to go after %d ms\n"</span>, dur.Milliseconds())

        <span class="hljs-comment">// Wait for all goroutines to reach the barrier.</span>
        b.Touch()

        <span class="hljs-comment">// Simulate the main step.</span>
        fmt.Println(<span class="hljs-string">"go!"</span>)
    })
}

wg.Wait()
fmt.Printf(<span class="hljs-string">"all done in %d ms\n"</span>, time.Since(start).Milliseconds())
</code></pre>
<pre><code>ready to go after <span class="hljs-number">10</span> ms
ready to go after <span class="hljs-number">20</span> ms
ready to go after <span class="hljs-number">30</span> ms
ready to go after <span class="hljs-number">40</span> ms
go!
go!
go!
go!
all done <span class="hljs-keyword">in</span> <span class="hljs-number">40</span> ms
</code></pre><p>Now the faster goroutines waited at the barrier for the slower ones, and only after that did they all move on to the main step together.</p>
<p>Here are some examples of when a synchronization barrier can be useful:</p>
<ul>
<li><em>Parallel computing</em>. If you're sorting in parallel and then merging the results, the sorting steps must finish before the merging starts. If you merge too soon, you'll get the wrong results.</li>
<li><em>Multiplayer applications</em>. If a duel in the game involves N players, all resources for those players need to be fully prepared before the duel begins. Otherwise, some players might be at a disadvantage.</li>
<li><em>Distributed systems</em>. To create a backup, you need to wait until all nodes in the system reach a consistent state (a checkpoint). Otherwise, the backup's integrity could be compromised.</li>
</ul>
<p>The standard library doesn't have a barrier, so now is a great time to make one yourself!</p>
<hr />
<h2 id="heading-summary">Summary</h2>
<p>You've learned the classic synchronization tools — mutexes, semaphores, rendezvous, and barriers. Be careful when using them. Try to avoid complicated setups, and always write tests for tricky concurrent situations.</p>
<p>Key points to remember:</p>
<ul>
<li><strong>Mutexes</strong> allow only one goroutine at a time to access a resource.</li>
<li><strong>Semaphores</strong> allow up to N goroutines to access a resource concurrently.</li>
<li><strong>Rendezvous</strong> enables two goroutines to wait for each other at a synchronization point.</li>
<li><strong>Synchronization barriers</strong> ensure all goroutines reach a point before any proceed.</li>
<li><strong>Channels</strong> can be used to implement semaphores and other synchronization primitives.</li>
</ul>
<p>These tools provide powerful ways to control concurrency and coordinate goroutines in your Go programs.</p>
]]></content:encoded></item><item><title><![CDATA[Go: Building Concurrent Data Processing Pipelines]]></title><description><![CDATA[Now that we understand how to use goroutines and channels, let's explore how to combine them into concurrent pipelines for efficient data processing.

Leaked Goroutines
Consider a function that sends numbers within a specified range to a channel:
fun...]]></description><link>https://blog.fshtab.com/go-pipelines-concurrent-data-processing</link><guid isPermaLink="true">https://blog.fshtab.com/go-pipelines-concurrent-data-processing</guid><dc:creator><![CDATA[Fedor S]]></dc:creator><pubDate>Sat, 18 May 2024 11:30:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1766406545387/a7b97e03-ea2e-4a4f-9ec2-e7c42137bc3d.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Now that we understand how to use goroutines and channels, let's explore how to combine them into concurrent pipelines for efficient data processing.</p>
<hr />
<h2 id="heading-leaked-goroutines">Leaked Goroutines</h2>
<p>Consider a function that sends numbers within a specified range to a channel:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">rangeGen</span><span class="hljs-params">(start, stop <span class="hljs-keyword">int</span>)</span> &lt;-<span class="hljs-title">chan</span> <span class="hljs-title">int</span></span> {
    out := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>)
    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">for</span> i := start; i &lt; stop; i++ {
            out &lt;- i
        }
        <span class="hljs-built_in">close</span>(out)
    }()
    <span class="hljs-keyword">return</span> out
}
</code></pre>
<p>It appears to work correctly:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    generated := rangeGen(<span class="hljs-number">41</span>, <span class="hljs-number">46</span>)
    <span class="hljs-keyword">for</span> val := <span class="hljs-keyword">range</span> generated {
        fmt.Println(val)
    }
}
</code></pre>
<pre><code><span class="hljs-number">41</span>
<span class="hljs-number">42</span>
<span class="hljs-number">43</span>
<span class="hljs-number">44</span>
<span class="hljs-number">45</span>
</code></pre><p>However, let's examine what happens if we exit the loop prematurely:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    generated := rangeGen(<span class="hljs-number">41</span>, <span class="hljs-number">46</span>)
    <span class="hljs-keyword">for</span> val := <span class="hljs-keyword">range</span> generated {
        fmt.Println(val)
        <span class="hljs-keyword">if</span> val == <span class="hljs-number">42</span> {
            <span class="hljs-keyword">break</span>
        }
    }
}
</code></pre>
<pre><code><span class="hljs-number">41</span>
<span class="hljs-number">42</span>
</code></pre><p>At first glance, it still works. But there's a problem — the <code>rangeGen()</code> goroutine becomes stuck:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">rangeGen</span><span class="hljs-params">(start, stop <span class="hljs-keyword">int</span>)</span> &lt;-<span class="hljs-title">chan</span> <span class="hljs-title">int</span></span> {
    out := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>)
    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">for</span> i := start; i &lt; stop; i++ {    <span class="hljs-comment">// (1)</span>
            out &lt;- i                       <span class="hljs-comment">// (2)</span>
        }
        <span class="hljs-built_in">close</span>(out)
    }()
    <span class="hljs-keyword">return</span> out
}
</code></pre>
<p>Since <code>main()</code> breaks its loop at number 42 and stops reading from the <code>generated</code> channel, the loop inside <code>rangeGen()</code> ➊ didn't complete. It got permanently blocked trying to send number 43 to the <code>out</code> channel ➋. The goroutine is stuck. The <code>out</code> channel didn't close, so if other goroutines depended on it, they would also get stuck.</p>
<p>In this case, it's not critical: when <code>main()</code> exits, the runtime will terminate all other goroutines. But if <code>main()</code> continued to run and called <code>rangeGen()</code> repeatedly, the leaked goroutines would accumulate. This is problematic: goroutines are lightweight but not completely "free". Eventually, you might run out of memory (the garbage collector doesn't collect goroutines).</p>
<p>We need a mechanism to terminate a goroutine early.</p>
<hr />
<h2 id="heading-cancel-channel">Cancel Channel</h2>
<p>First, we'll create a separate <em>cancel channel</em> through which <code>main()</code> will signal <code>rangeGen()</code> to exit:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    cancel := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">struct</span>{})    <span class="hljs-comment">// (1)</span>
    <span class="hljs-keyword">defer</span> <span class="hljs-built_in">close</span>(cancel)              <span class="hljs-comment">// (2)</span>

    generated := rangeGen(cancel, <span class="hljs-number">41</span>, <span class="hljs-number">46</span>)    <span class="hljs-comment">// (3)</span>
    <span class="hljs-keyword">for</span> val := <span class="hljs-keyword">range</span> generated {
        fmt.Println(val)
        <span class="hljs-keyword">if</span> val == <span class="hljs-number">42</span> {
            <span class="hljs-keyword">break</span>
        }
    }
}
</code></pre>
<p>We create a <code>cancel</code> channel ➊ and immediately set up a deferred <code>close(cancel)</code> ➋. This is a common practice to avoid tracking every place in the code where the channel needs to be closed. <code>defer</code> ensures that the channel is closed when the function exits, so you don't have to worry about it.</p>
<p>Next, we pass the <code>cancel</code> channel to the goroutine ➌. Now, when the channel closes, the goroutine needs to detect this and exit. Ideally, you'd add a check like this:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">rangeGen</span><span class="hljs-params">(cancel &lt;-<span class="hljs-keyword">chan</span> <span class="hljs-keyword">struct</span>{}, start, stop <span class="hljs-keyword">int</span>)</span> &lt;-<span class="hljs-title">chan</span> <span class="hljs-title">int</span></span> {
    out := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>)
    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">defer</span> <span class="hljs-built_in">close</span>(out)
        <span class="hljs-keyword">for</span> i := start; i &lt; stop; i++ {
            out &lt;- i
            <span class="hljs-keyword">if</span> &lt;-cancel == <span class="hljs-keyword">struct</span>{}{} {    <span class="hljs-comment">// (1)</span>
                <span class="hljs-keyword">return</span>
            }
        }
    }()
    <span class="hljs-keyword">return</span> out
}
</code></pre>
<pre><code>fatal error: all goroutines are asleep - deadlock!
</code></pre><p>If <code>cancel</code> is closed, the check ➊ will pass (a closed channel always returns a zero value, remember?), and the goroutine will exit. However, if <code>cancel</code> isn't closed, the goroutine would block and not continue to the next loop iteration.</p>
<p>We need a different, non-blocking approach:</p>
<ul>
<li>If <code>cancel</code> is closed, exit the goroutine;</li>
<li>Otherwise, send the next value to <code>out</code>.</li>
</ul>
<p>Go has a <em>select</em> statement for this:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">rangeGen</span><span class="hljs-params">(cancel &lt;-<span class="hljs-keyword">chan</span> <span class="hljs-keyword">struct</span>{}, start, stop <span class="hljs-keyword">int</span>)</span> &lt;-<span class="hljs-title">chan</span> <span class="hljs-title">int</span></span> {
    out := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>)
    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">defer</span> <span class="hljs-built_in">close</span>(out)
        <span class="hljs-keyword">for</span> i := start; i &lt; stop; i++ {
            <span class="hljs-keyword">select</span> {
            <span class="hljs-keyword">case</span> out &lt;- i:    <span class="hljs-comment">// (1)</span>
            <span class="hljs-keyword">case</span> &lt;-cancel:    <span class="hljs-comment">// (2)</span>
                <span class="hljs-keyword">return</span>
            }
        }
    }()
    <span class="hljs-keyword">return</span> out
}
</code></pre>
<pre><code><span class="hljs-number">41</span>
<span class="hljs-number">42</span>
</code></pre><p><code>select</code> is somewhat like <code>switch</code>, but specifically designed for channels. Here's what it does:</p>
<ul>
<li>Checks which cases are not blocked.</li>
<li>If multiple cases are ready, randomly selects one to execute.</li>
<li>If all cases are blocked, waits until one is ready.</li>
</ul>
<p>In our case, while <code>cancel</code> is open, its case ➋ is blocked (you can't read from a channel if no one is writing to it). However, the <code>out &lt;- i</code> case ➊ is unblocked because <code>main()</code> is reading from <code>out</code>. So, <code>select</code> will execute <code>out &lt;- i</code> in each loop iteration.</p>
<p>Then <code>main()</code> will reach number 42 and stop reading from <code>out</code>. After that, both <code>select</code> cases will block, and the goroutine will (temporarily) hang.</p>
<p>Finally, <code>main()</code> will execute the deferred <code>close(cancel)</code>, which will unblock the second <code>select</code> case ➋, and the goroutine will exit. The <code>out</code> channel will close too, thanks to <code>defer</code>.</p>
<p>If <code>main()</code> decides not to stop at 42 and continues to read all values, the cancel channel approach will still work correctly:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    cancel := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">struct</span>{})
    <span class="hljs-keyword">defer</span> <span class="hljs-built_in">close</span>(cancel)

    generated := rangeGen(cancel, <span class="hljs-number">41</span>, <span class="hljs-number">46</span>)
    <span class="hljs-keyword">for</span> val := <span class="hljs-keyword">range</span> generated {
        fmt.Println(val)
    }
}
</code></pre>
<pre><code><span class="hljs-number">41</span>
<span class="hljs-number">42</span>
<span class="hljs-number">43</span>
<span class="hljs-number">44</span>
<span class="hljs-number">45</span>
</code></pre><p>Here, <code>rangeGen()</code> will finish before <code>main()</code> calls <code>close(cancel)</code>. Which is perfectly fine.</p>
<p>So thanks to the cancel channel and the select statement, the <code>rangeGen()</code> goroutine will exit correctly regardless of what happens in <code>main()</code>. No more leaked goroutines!</p>
<hr />
<h2 id="heading-cancel-vs-done">Cancel vs. Done</h2>
<p>The cancel channel is similar to the done channel that we covered in previous chapters.</p>
<p><strong>Done channel:</strong></p>
<pre><code class="lang-go"><span class="hljs-comment">// Goroutine B receives a channel to signal</span>
<span class="hljs-comment">// when it has finished its work.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">b</span><span class="hljs-params">(done <span class="hljs-keyword">chan</span>&lt;- <span class="hljs-keyword">struct</span>{})</span></span> {
    <span class="hljs-comment">// do work...</span>
    done &lt;- <span class="hljs-keyword">struct</span>{}{}
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">a</span><span class="hljs-params">()</span></span> {
    done := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">struct</span>{})
    <span class="hljs-keyword">go</span> b(done)
    <span class="hljs-comment">// Goroutine A waits for B to finish its work.</span>
    &lt;-done
}
</code></pre>
<p><strong>Cancel channel:</strong></p>
<pre><code class="lang-go"><span class="hljs-comment">// Goroutine B receives a channel</span>
<span class="hljs-comment">// to get a cancel signal.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">b</span><span class="hljs-params">(cancel &lt;-<span class="hljs-keyword">chan</span> <span class="hljs-keyword">struct</span>{})</span></span> {
    <span class="hljs-comment">// do work...</span>
    <span class="hljs-keyword">select</span> {
    <span class="hljs-keyword">case</span> &lt;-cancel:
        <span class="hljs-keyword">return</span>
    }
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">a</span><span class="hljs-params">()</span></span> {
    cancel := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">struct</span>{})
    <span class="hljs-keyword">go</span> b(cancel)
    <span class="hljs-comment">// Goroutine A signals to B</span>
    <span class="hljs-comment">// that it is time to stop.</span>
    <span class="hljs-built_in">close</span>(cancel)
}
</code></pre>
<p>In practice, both cancel and done channels are often named "done", so don't be surprised. To avoid confusion, we'll use "cancel" for cancellation and "done" for completion.</p>
<hr />
<h2 id="heading-merging-channels">Merging Channels</h2>
<p>Sometimes several independent functions send results to their own channels. But it's more convenient to work with a single result channel. So you need to merge the output channels of these functions into a single channel.</p>
<h3 id="heading-sequential-merging">Sequential Merging</h3>
<p>The simplest approach is to merge channels sequentially:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">merge</span><span class="hljs-params">(ch1, ch2 &lt;-<span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>)</span> &lt;-<span class="hljs-title">chan</span> <span class="hljs-title">int</span></span> {
    out := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>)
    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">defer</span> <span class="hljs-built_in">close</span>(out)
        <span class="hljs-keyword">for</span> val := <span class="hljs-keyword">range</span> ch1 {
            out &lt;- val
        }
        <span class="hljs-keyword">for</span> val := <span class="hljs-keyword">range</span> ch2 {
            out &lt;- val
        }
    }()
    <span class="hljs-keyword">return</span> out
}
</code></pre>
<p>This works, but it processes channels one after another, which may not be optimal for concurrent processing.</p>
<h3 id="heading-concurrent-merging">Concurrent Merging</h3>
<p>A better approach is to merge channels concurrently:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">merge</span><span class="hljs-params">(ch1, ch2 &lt;-<span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>)</span> &lt;-<span class="hljs-title">chan</span> <span class="hljs-title">int</span></span> {
    out := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>)
    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">defer</span> <span class="hljs-built_in">close</span>(out)
        <span class="hljs-keyword">var</span> wg sync.WaitGroup
        wg.Add(<span class="hljs-number">2</span>)

        <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
            <span class="hljs-keyword">defer</span> wg.Done()
            <span class="hljs-keyword">for</span> val := <span class="hljs-keyword">range</span> ch1 {
                out &lt;- val
            }
        }()

        <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
            <span class="hljs-keyword">defer</span> wg.Done()
            <span class="hljs-keyword">for</span> val := <span class="hljs-keyword">range</span> ch2 {
                out &lt;- val
            }
        }()

        wg.Wait()
    }()
    <span class="hljs-keyword">return</span> out
}
</code></pre>
<p>This approach processes both channels simultaneously, which is more efficient.</p>
<h3 id="heading-merging-with-select">Merging with Select</h3>
<p>For multiple channels, you can use <code>select</code>:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">merge</span><span class="hljs-params">(channels ...&lt;-<span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>)</span> &lt;-<span class="hljs-title">chan</span> <span class="hljs-title">int</span></span> {
    out := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>)
    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">defer</span> <span class="hljs-built_in">close</span>(out)
        <span class="hljs-keyword">var</span> wg sync.WaitGroup
        wg.Add(<span class="hljs-built_in">len</span>(channels))

        <span class="hljs-keyword">for</span> _, ch := <span class="hljs-keyword">range</span> channels {
            <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(c &lt;-<span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>)</span></span> {
                <span class="hljs-keyword">defer</span> wg.Done()
                <span class="hljs-keyword">for</span> val := <span class="hljs-keyword">range</span> c {
                    out &lt;- val
                }
            }(ch)
        }

        wg.Wait()
    }()
    <span class="hljs-keyword">return</span> out
}
</code></pre>
<hr />
<h2 id="heading-pipelines">Pipelines</h2>
<p>A pipeline is a series of stages connected by channels, where each stage is a group of goroutines running the same function. In each stage, the goroutines:</p>
<ul>
<li>Receive values from upstream via inbound channels</li>
<li>Perform some function on that data, usually producing new values</li>
<li>Send values downstream via outbound channels</li>
</ul>
<p>Each stage has any number of inbound and outbound channels, except the first and last stages, which have only outbound or inbound channels, respectively. The first stage is sometimes called the source or producer; the last stage, the sink or consumer.</p>
<p>Here's a simple pipeline example:</p>
<p><strong>The "generate" stage:</strong></p>
<pre><code class="lang-go"><span class="hljs-comment">// generate sends numbers from 1 to stop inclusive.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">generate</span><span class="hljs-params">(stop <span class="hljs-keyword">int</span>)</span> &lt;-<span class="hljs-title">chan</span> <span class="hljs-title">int</span></span> {
    out := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>)
    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">defer</span> <span class="hljs-built_in">close</span>(out)
        <span class="hljs-keyword">for</span> i := <span class="hljs-number">1</span>; i &lt;= stop; i++ {
            out &lt;- i
        }
    }()
    <span class="hljs-keyword">return</span> out
}
</code></pre>
<p><strong>The "calculate" stage:</strong></p>
<pre><code class="lang-go"><span class="hljs-comment">// Answer represents the result of a calculation.</span>
<span class="hljs-keyword">type</span> Answer <span class="hljs-keyword">struct</span> {
    x, y <span class="hljs-keyword">int</span>
}

<span class="hljs-comment">// calculate produces answers for the given numbers.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">calculate</span><span class="hljs-params">(in &lt;-<span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>)</span> &lt;-<span class="hljs-title">chan</span> <span class="hljs-title">Answer</span></span> {
    out := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> Answer)
    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">defer</span> <span class="hljs-built_in">close</span>(out)
        <span class="hljs-keyword">for</span> n := <span class="hljs-keyword">range</span> in {
            out &lt;- fetchAnswer(n)
        }
    }()
    <span class="hljs-keyword">return</span> out
}
</code></pre>
<p><strong>The "print" stage (in the main goroutine for simplicity):</strong></p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    inputs := generate(<span class="hljs-number">5</span>)
    answers := calculate(inputs)
    <span class="hljs-keyword">for</span> ans := <span class="hljs-keyword">range</span> answers {
        fmt.Printf(<span class="hljs-string">"%d -&gt; %d\n"</span>, ans.x, ans.y)
    }
}
</code></pre>
<pre><code><span class="hljs-number">1</span> -&gt; <span class="hljs-number">1</span>
<span class="hljs-number">2</span> -&gt; <span class="hljs-number">4</span>
<span class="hljs-number">3</span> -&gt; <span class="hljs-number">9</span>
<span class="hljs-number">4</span> -&gt; <span class="hljs-number">16</span>
<span class="hljs-number">5</span> -&gt; <span class="hljs-number">25</span>
</code></pre><hr />
<h2 id="heading-preventing-goroutine-leaks">Preventing Goroutine Leaks</h2>
<p>To prevent goroutine leaks in pipelines, we need to ensure that:</p>
<ol>
<li>All channels are properly closed when no longer needed</li>
<li>Goroutines can be cancelled when the pipeline is no longer needed</li>
<li>We use <code>defer close()</code> to ensure cleanup happens</li>
</ol>
<p>Here's an improved version with cancellation support:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">generate</span><span class="hljs-params">(cancel &lt;-<span class="hljs-keyword">chan</span> <span class="hljs-keyword">struct</span>{}, stop <span class="hljs-keyword">int</span>)</span> &lt;-<span class="hljs-title">chan</span> <span class="hljs-title">int</span></span> {
    out := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>)
    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">defer</span> <span class="hljs-built_in">close</span>(out)
        <span class="hljs-keyword">for</span> i := <span class="hljs-number">1</span>; i &lt;= stop; i++ {
            <span class="hljs-keyword">select</span> {
            <span class="hljs-keyword">case</span> out &lt;- i:
            <span class="hljs-keyword">case</span> &lt;-cancel:
                <span class="hljs-keyword">return</span>
            }
        }
    }()
    <span class="hljs-keyword">return</span> out
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">calculate</span><span class="hljs-params">(cancel &lt;-<span class="hljs-keyword">chan</span> <span class="hljs-keyword">struct</span>{}, in &lt;-<span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>)</span> &lt;-<span class="hljs-title">chan</span> <span class="hljs-title">Answer</span></span> {
    out := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> Answer)
    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">defer</span> <span class="hljs-built_in">close</span>(out)
        <span class="hljs-keyword">for</span> {
            <span class="hljs-keyword">select</span> {
            <span class="hljs-keyword">case</span> n, ok := &lt;-in:
                <span class="hljs-keyword">if</span> !ok {
                    <span class="hljs-keyword">return</span>
                }
                <span class="hljs-keyword">select</span> {
                <span class="hljs-keyword">case</span> out &lt;- fetchAnswer(n):
                <span class="hljs-keyword">case</span> &lt;-cancel:
                    <span class="hljs-keyword">return</span>
                }
            <span class="hljs-keyword">case</span> &lt;-cancel:
                <span class="hljs-keyword">return</span>
            }
        }
    }()
    <span class="hljs-keyword">return</span> out
}
</code></pre>
<hr />
<h2 id="heading-error-handling">Error Handling</h2>
<p>The <code>fetchAnswer</code> function is responsible for retrieving an answer for a given number from a remote API:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">fetchAnswer</span><span class="hljs-params">(n <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">Answer</span></span> {
    <span class="hljs-comment">// ...</span>
}
</code></pre>
<p>Hoping that a remote service will <em>always</em> work properly is a bit unrealistic. We have to account for errors:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">fetchAnswer</span><span class="hljs-params">(n <span class="hljs-keyword">int</span>)</span> <span class="hljs-params">(Answer, error)</span></span> {
    <span class="hljs-comment">// ...</span>
}
</code></pre>
<p>But what should we do with these errors (if any)? There is no place for them in <code>calculate</code>.</p>
<p>As it turns out, we have three options.</p>
<h3 id="heading-return-on-first-error">Return on First Error</h3>
<p>If we don't tolerate errors, the easiest thing to do is to return from <code>calculate</code> as soon as <code>fetchAnswer</code> encounters an error. Since the <code>out</code> channel only accepts <code>Answer</code>s, let's add a separate <code>errc</code> channel with a place for a single error:</p>
<pre><code class="lang-go"><span class="hljs-comment">// calculate produces answers for the given numbers.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">calculate</span><span class="hljs-params">(in &lt;-<span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>)</span> <span class="hljs-params">(&lt;-<span class="hljs-keyword">chan</span> Answer, &lt;-<span class="hljs-keyword">chan</span> error)</span></span> {
    out := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> Answer)
    errc := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> error, <span class="hljs-number">1</span>)  <span class="hljs-comment">// (1)</span>
    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">defer</span> <span class="hljs-built_in">close</span>(out)
        <span class="hljs-keyword">for</span> n := <span class="hljs-keyword">range</span> in {
            ans, err := fetchAnswer(n)
            <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
                errc &lt;- err      <span class="hljs-comment">// (2)</span>
                <span class="hljs-keyword">return</span>
            }
            out &lt;- ans
        }
        errc &lt;- <span class="hljs-literal">nil</span>              <span class="hljs-comment">// (3)</span>
    }()
    <span class="hljs-keyword">return</span> out, errc
}
</code></pre>
<p>The error channel is buffered with a capacity of one ➊. As a result of <code>calculate</code> execution, it will contain either an actual error ➋ or nil ➌, depending on the results of the remote call in <code>fetchAnswer</code>.</p>
<p>Since <code>errc</code> is guaranteed to contain a value (an error or nil), we can read from it in the next pipeline step without using select:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    inputs := generate(<span class="hljs-number">5</span>)
    answers, errs := calculate(inputs)

    <span class="hljs-keyword">for</span> ans := <span class="hljs-keyword">range</span> answers {
        fmt.Printf(<span class="hljs-string">"%d -&gt; %d\n"</span>, ans.x, ans.y)
    }
    <span class="hljs-keyword">if</span> err := &lt;-errs; err != <span class="hljs-literal">nil</span> {
        fmt.Println(<span class="hljs-string">"error:"</span>, err)
    }
}
</code></pre>
<pre><code><span class="hljs-number">1</span> -&gt; <span class="hljs-number">1</span>
<span class="hljs-attr">error</span>: bad number
</code></pre><p>But what if we don't want to stop the whole pipeline because of a single error? Enter the next option.</p>
<h3 id="heading-composite-result-type">Composite Result Type</h3>
<p>Let's get rid of the error channel and return an error along with the answer. To do this, we will introduce a separate <em>result</em> type:</p>
<pre><code class="lang-go"><span class="hljs-comment">// Result contains an answer or an error.</span>
<span class="hljs-keyword">type</span> Result <span class="hljs-keyword">struct</span> {
    answer Answer
    err    error
}
</code></pre>
<p>Now <code>calculate</code> can send result values to the output channel:</p>
<pre><code class="lang-go"><span class="hljs-comment">// calculate produces answers for the given numbers.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">calculate</span><span class="hljs-params">(in &lt;-<span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>)</span> &lt;-<span class="hljs-title">chan</span> <span class="hljs-title">Result</span></span> {
    out := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> Result)
    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">defer</span> <span class="hljs-built_in">close</span>(out)
        <span class="hljs-keyword">for</span> n := <span class="hljs-keyword">range</span> in {
            ans, err := fetchAnswer(n)
            out &lt;- Result{ans, err}
        }
    }()
    <span class="hljs-keyword">return</span> out
}
</code></pre>
<p>And the next pipeline step can process these results however it wants:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    inputs := generate(<span class="hljs-number">5</span>)
    results := calculate(inputs)
    <span class="hljs-keyword">for</span> res := <span class="hljs-keyword">range</span> results {
        <span class="hljs-keyword">if</span> res.err == <span class="hljs-literal">nil</span> {
            fmt.Printf(<span class="hljs-string">"%d -&gt; %d\n"</span>, res.answer.x, res.answer.y)
        } <span class="hljs-keyword">else</span> {
            fmt.Printf(<span class="hljs-string">"%d -&gt; error: %s\n"</span>, res.answer.x, res.err)
        }
    }
}
</code></pre>
<pre><code><span class="hljs-number">1</span> -&gt; <span class="hljs-number">1</span>
<span class="hljs-number">2</span> -&gt; error: bad number
<span class="hljs-number">3</span> -&gt; <span class="hljs-number">9</span>
<span class="hljs-number">4</span> -&gt; error: bad number
<span class="hljs-number">5</span> -&gt; <span class="hljs-number">25</span>
</code></pre><p>We don't have to introduce a separate result type for each possible pipeline step in our program. A single generic <code>Result</code> will suffice:</p>
<pre><code class="lang-go"><span class="hljs-comment">// Result contains a value or an error.</span>
<span class="hljs-keyword">type</span> Result[T any] <span class="hljs-keyword">struct</span> {
    val T
    err error
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(r Result[T])</span> <span class="hljs-title">OK</span><span class="hljs-params">()</span> <span class="hljs-title">bool</span></span> {
    <span class="hljs-keyword">return</span> r.err == <span class="hljs-literal">nil</span>
}
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(r Result[T])</span> <span class="hljs-title">Val</span><span class="hljs-params">()</span> <span class="hljs-title">T</span></span> {
    <span class="hljs-keyword">return</span> r.val
}
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(r Result[T])</span> <span class="hljs-title">Err</span><span class="hljs-params">()</span> <span class="hljs-title">error</span></span> {
    <span class="hljs-keyword">return</span> r.err
}
</code></pre>
<pre><code class="lang-go"><span class="hljs-comment">// calculate produces answers for the given numbers.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">calculate</span><span class="hljs-params">(in &lt;-<span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>)</span> &lt;-<span class="hljs-title">chan</span> <span class="hljs-title">Result</span>[<span class="hljs-title">Answer</span>]</span> {
    <span class="hljs-comment">// ...</span>
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    inputs := generate(<span class="hljs-number">5</span>)
    results := calculate(inputs)
    <span class="hljs-keyword">for</span> res := <span class="hljs-keyword">range</span> results {
        <span class="hljs-keyword">if</span> res.OK() {
            fmt.Printf(<span class="hljs-string">"✓ %v\n"</span>, res.Val())
        } <span class="hljs-keyword">else</span> {
            fmt.Printf(<span class="hljs-string">"✗ %v\n"</span>, res.Err())
        }
    }
}
</code></pre>
<h3 id="heading-collect-errors-separately">Collect Errors Separately</h3>
<p>Let's say we don't want to bother handling errors from individual pipeline stages. What we want is a single error collector for the entire pipeline. For simplicity, it'll just log all errors:</p>
<pre><code class="lang-go"><span class="hljs-comment">// collectErrors prints all incoming errors.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">collectErrors</span><span class="hljs-params">(in &lt;-<span class="hljs-keyword">chan</span> error)</span> &lt;-<span class="hljs-title">chan</span> <span class="hljs-title">struct</span></span>{} {
    done := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">struct</span>{})
    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">defer</span> <span class="hljs-built_in">close</span>(done)
        <span class="hljs-keyword">for</span> err := <span class="hljs-keyword">range</span> in {
            fmt.Printf(<span class="hljs-string">"error: %s\n"</span>, err)
        }
    }()
    <span class="hljs-keyword">return</span> done
}
</code></pre>
<p>Since there will be a single error channel for all pipeline stages, we'll create it in <code>main</code> and pass it to each of the pipeline steps:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    errc := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> error)
    done := collectErrors(errc)

    inputs := generate(<span class="hljs-number">5</span>, errc)
    answers := calculate(inputs, errc)

    <span class="hljs-keyword">for</span> ans := <span class="hljs-keyword">range</span> answers {
        fmt.Printf(<span class="hljs-string">"%d -&gt; %d\n"</span>, ans.x, ans.y)
    }

    <span class="hljs-built_in">close</span>(errc)
    &lt;-done
}
</code></pre>
<p>At each stage of the pipeline, we'll send any errors we encounter to the error channel:</p>
<pre><code class="lang-go"><span class="hljs-comment">// calculate produces answers for the given numbers.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">calculate</span><span class="hljs-params">(in &lt;-<span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>, errc <span class="hljs-keyword">chan</span>&lt;- error)</span> &lt;-<span class="hljs-title">chan</span> <span class="hljs-title">Answer</span></span> {
    out := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> Answer)
    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">defer</span> <span class="hljs-built_in">close</span>(out)
        <span class="hljs-keyword">for</span> n := <span class="hljs-keyword">range</span> in {
            ans, err := fetchAnswer(n)
            <span class="hljs-keyword">if</span> err == <span class="hljs-literal">nil</span> {
                out &lt;- ans
            } <span class="hljs-keyword">else</span> {
                errc &lt;- err
            }
        }
    }()
    <span class="hljs-keyword">return</span> out
}
</code></pre>
<pre><code><span class="hljs-number">1</span> -&gt; <span class="hljs-number">1</span>
<span class="hljs-number">3</span> -&gt; <span class="hljs-number">9</span>
<span class="hljs-attr">error</span>: bad number
<span class="hljs-attr">error</span>: bad number
<span class="hljs-number">5</span> -&gt; <span class="hljs-number">25</span>
</code></pre><p>Works like a charm. But there is one caveat: since errors are no longer tied to answers, we do not know which numbers caused the remote API to fail. Of course, we can add the necessary information to the error text, or even create a richer error type, so it's probably not a big deal.</p>
<p>Also, the error collector should be reasonably fast, so that it does not slow down (or even block) the normal pipeline flow in case of occasional errors. We can add a buffer to the error channel and use select, just to be sure:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-comment">// A buffered channel to queue up to 100 errors.</span>
    errc := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> error, <span class="hljs-number">100</span>)
    <span class="hljs-comment">// ...</span>
}

<span class="hljs-comment">// calculate produces answers for the given numbers.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">calculate</span><span class="hljs-params">(in &lt;-<span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>, errc <span class="hljs-keyword">chan</span>&lt;- error)</span> &lt;-<span class="hljs-title">chan</span> <span class="hljs-title">Answer</span></span> {
    out := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> Answer)
    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">defer</span> <span class="hljs-built_in">close</span>(out)
        <span class="hljs-keyword">for</span> n := <span class="hljs-keyword">range</span> in {
            ans, err := fetchAnswer(n)
            <span class="hljs-keyword">if</span> err == <span class="hljs-literal">nil</span> {
                out &lt;- ans
            } <span class="hljs-keyword">else</span> {
                <span class="hljs-keyword">select</span> {
                <span class="hljs-keyword">case</span> errc &lt;- err:
                <span class="hljs-keyword">default</span>:
                    <span class="hljs-comment">// If errc is full, drop the error.</span>
                }
            }
        }
    }()
    <span class="hljs-keyword">return</span> out
}
</code></pre>
<p>This approach is quite rare. Using a result type is more common in practice.</p>
<hr />
<h2 id="heading-summary">Summary</h2>
<p>Pipelines are one of the most common uses of concurrency in real-world programs. Now you know how to:</p>
<ul>
<li>Combine pipelines of independent blocks.</li>
<li>Split and merge data streams.</li>
<li>Cancel pipeline stages.</li>
<li>Prevent goroutine leaks.</li>
<li>Handle pipeline step errors.</li>
</ul>
<p>Pipelines provide a powerful way to process data concurrently while maintaining clear structure and error handling capabilities.</p>
]]></content:encoded></item><item><title><![CDATA[Go: Preventing Data Races and Ensuring Thread-Safe Access]]></title><description><![CDATA[What happens if multiple goroutines modify the same data structure? Sadly, nothing good. Let's learn more about it.

Concurrent Modification
So far, our goroutines haven't gotten in each other's way. They've used channels to exchange data, which is s...]]></description><link>https://blog.fshtab.com/go-data-races-safe-concurrent-access</link><guid isPermaLink="true">https://blog.fshtab.com/go-data-races-safe-concurrent-access</guid><dc:creator><![CDATA[Fedor S]]></dc:creator><pubDate>Thu, 25 Apr 2024 10:45:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1766406537254/39702e36-1ad7-48a1-a1f5-767d5118247a.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>What happens if multiple goroutines modify the same data structure? Sadly, nothing good. Let's learn more about it.</p>
<hr />
<h2 id="heading-concurrent-modification">Concurrent Modification</h2>
<p>So far, our goroutines haven't gotten in each other's way. They've used channels to exchange data, which is safe. But what happens if several goroutines try to access the same object at the same time? Let's find out.</p>
<p>Let's write a program that counts word frequencies:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-comment">// generate creates 100 words, each 3 letters long,</span>
    <span class="hljs-comment">// and sends them to the channel.</span>
    in := generate(<span class="hljs-number">100</span>, <span class="hljs-number">3</span>)

    <span class="hljs-keyword">var</span> wg sync.WaitGroup
    wg.Add(<span class="hljs-number">2</span>)

    <span class="hljs-comment">// count reads words from the input channel</span>
    <span class="hljs-comment">// and counts how often each one appears.</span>
    count := <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(counter <span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]<span class="hljs-keyword">int</span>)</span></span> {
        <span class="hljs-keyword">defer</span> wg.Done()
        <span class="hljs-keyword">for</span> word := <span class="hljs-keyword">range</span> in {
            counter[word]++
        }
    }

    counter := <span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]<span class="hljs-keyword">int</span>{}
    <span class="hljs-keyword">go</span> count(counter)
    <span class="hljs-keyword">go</span> count(counter)
    wg.Wait()

    fmt.Println(counter)
}
</code></pre>
<pre><code>fatal error: concurrent map writes

goroutine <span class="hljs-number">1</span> [sync.WaitGroup.Wait]:
sync.runtime_SemacquireWaitGroup(<span class="hljs-number">0x140000021c0</span>?)

goroutine <span class="hljs-number">34</span> [chan send]:
main.generate.func1()

goroutine <span class="hljs-number">35</span> [running]:
internal/runtime/maps.fatal({<span class="hljs-number">0x104b4039e</span>?, <span class="hljs-number">0x14000038a08</span>?})

goroutine <span class="hljs-number">36</span> [runnable]:
internal/runtime/maps.newTable(<span class="hljs-number">0x104b78340</span>, <span class="hljs-number">0x80</span>, <span class="hljs-number">0x0</span>, <span class="hljs-number">0x0</span>)
</code></pre><p>What is generate:</p>
<pre><code class="lang-go"><span class="hljs-comment">// generate creates nWords words, each wordLen letters long,</span>
<span class="hljs-comment">// and sends them to the channel.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">generate</span><span class="hljs-params">(nWords, wordLen <span class="hljs-keyword">int</span>)</span> &lt;-<span class="hljs-title">chan</span> <span class="hljs-title">string</span></span> {
    out := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">string</span>)
    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">defer</span> <span class="hljs-built_in">close</span>(out)
        <span class="hljs-keyword">for</span> ; nWords &gt; <span class="hljs-number">0</span>; nWords-- {
            out &lt;- randomWord(wordLen)
        }
    }()
    <span class="hljs-keyword">return</span> out
}

<span class="hljs-comment">// randomWord returns a random word with n letters.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">randomWord</span><span class="hljs-params">(n <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">string</span></span> {
    <span class="hljs-keyword">const</span> vowels = <span class="hljs-string">"eaiou"</span>
    <span class="hljs-keyword">const</span> consonants = <span class="hljs-string">"rtnslcdpm"</span>
    chars := <span class="hljs-built_in">make</span>([]<span class="hljs-keyword">byte</span>, n)
    <span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i &lt; n; i += <span class="hljs-number">2</span> {
        chars[i] = consonants[rand.IntN(<span class="hljs-built_in">len</span>(consonants))]
    }
    <span class="hljs-keyword">for</span> i := <span class="hljs-number">1</span>; i &lt; n; i += <span class="hljs-number">2</span> {
        chars[i] = vowels[rand.IntN(<span class="hljs-built_in">len</span>(vowels))]
    }
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">string</span>(chars)
}
</code></pre>
<p><code>generate()</code> generates words and sends them to the <code>in</code> channel. <code>main()</code> creates an empty map called <code>counter</code> and passes it to two <code>count()</code> goroutines. <code>count()</code> reads from the <code>in</code> channel and fills the map with word counts. In the end, <code>counter</code> should contain the frequency of each word.</p>
<p>Let's run it:</p>
<pre><code>map[cec:<span class="hljs-number">1</span> ... nol:<span class="hljs-number">2</span> not:<span class="hljs-number">3</span> ... tut:<span class="hljs-number">1</span>]
</code></pre><p>And once again, just in case:</p>
<pre><code>fatal error: concurrent map writes

goroutine <span class="hljs-number">1</span> [sync.WaitGroup.Wait]:
sync.runtime_SemacquireWaitGroup(<span class="hljs-number">0x140000021c0</span>?)

goroutine <span class="hljs-number">34</span> [chan send]:
main.generate.func1()

goroutine <span class="hljs-number">35</span> [running]:
internal/runtime/maps.fatal({<span class="hljs-number">0x104b4039e</span>?, <span class="hljs-number">0x14000038a08</span>?})

goroutine <span class="hljs-number">36</span> [runnable]:
internal/runtime/maps.newTable(<span class="hljs-number">0x104b78340</span>, <span class="hljs-number">0x80</span>, <span class="hljs-number">0x0</span>, <span class="hljs-number">0x0</span>)
</code></pre><p>Panic!</p>
<p>Go doesn't let multiple goroutines write to a map at the same time. At first, this might seem odd. Here's the only operation that the <code>count()</code> goroutine does with the map:</p>
<pre><code class="lang-go">counter[word]++
</code></pre>
<p>Looks like an atomic action. Why not perform it from multiple goroutines?</p>
<p>The problem is that the action only seems atomic. The operation "increase the key value in the map" actually involves several smaller steps. If one goroutine does some of these steps and another goroutine does the rest, the map can get messed up. That's what the runtime is warning us about.</p>
<hr />
<h2 id="heading-data-race">Data Race</h2>
<p>When multiple goroutines access the same variable at the same time, and at least one of them changes it, it's called a <em>data race</em>. Concurrent map modification in the previous section is an example of a data race.</p>
<p>A data race doesn't always cause a runtime panic (the map example in the previous section is a nice exception: Go's map implementation has built-in runtime checks that can catch some data races). That's why Go provides a special tool called the <em>race detector</em>. You can turn it on with the <code>race</code> flag, which works with the <code>test</code>, <code>run</code>, <code>build</code>, and <code>install</code> commands.</p>
<blockquote>
<p>To use Go's race detector, you'll need to install gcc, the C compiler.</p>
</blockquote>
<p>For example, take this program:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">var</span> total <span class="hljs-keyword">int</span>
    <span class="hljs-keyword">var</span> wg sync.WaitGroup

    wg.Go(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        total++
    })
    wg.Go(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        total++
    })

    wg.Wait()
    fmt.Println(total)
}
</code></pre>
<pre><code><span class="hljs-number">2</span>
</code></pre><p>At first glance, it seems to work correctly. But actually, it has a data race:</p>
<pre><code class="lang-text">go run -race race.go
</code></pre>
<pre><code>==================
WARNING: DATA RACE
Read at <span class="hljs-number">0x00c000112038</span> by goroutine <span class="hljs-number">6</span>:
  main.main.func1()
      race.go:<span class="hljs-number">16</span> +<span class="hljs-number">0x74</span>

Previous write at <span class="hljs-number">0x00c000112038</span> by goroutine <span class="hljs-number">7</span>:
  main.main.func2()
      race.go:<span class="hljs-number">21</span> +<span class="hljs-number">0x84</span>

Goroutine <span class="hljs-number">6</span> (running) created at:
  main.main()
      race.go:<span class="hljs-number">14</span> +<span class="hljs-number">0x104</span>

Goroutine <span class="hljs-number">7</span> (finished) created at:
  main.main()
      race.go:<span class="hljs-number">19</span> +<span class="hljs-number">0x1a4</span>
==================
<span class="hljs-number">2</span>
Found <span class="hljs-number">1</span> data race(s)
</code></pre><blockquote>
<p>If you're wondering why a data race is a problem for a simple operation like <code>total++</code> — we'll cover it later in the chapter on atomic operations.</p>
</blockquote>
<p>Channels, on the other hand, are safe for concurrent reading and writing, and they don't cause data races:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    ch := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>, <span class="hljs-number">2</span>)
    <span class="hljs-keyword">var</span> wg sync.WaitGroup

    wg.Go(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        ch &lt;- <span class="hljs-number">1</span>
    })
    wg.Go(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        ch &lt;- <span class="hljs-number">1</span>
    })

    wg.Wait()
    fmt.Println(&lt;-ch + &lt;-ch)
}
</code></pre>
<pre><code><span class="hljs-number">2</span>
</code></pre><p>Data races are dangerous because they're hard to spot. Your program might work fine a hundred times, but on the hundred and first try, it could give the wrong result. Always check your code with a race detector.</p>
<hr />
<h2 id="heading-sequential-modification">Sequential Modification</h2>
<p>You can often rewrite a program to avoid concurrent modifications. Here is a possible approach for our word frequency program:</p>
<ul>
<li>Each <code>count()</code> goroutine counts frequencies in its own map.</li>
<li>A separate <code>merge()</code> function goes through the frequency maps and builds the final map.</li>
</ul>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-comment">// generate creates 100 words, each 3 letters long,</span>
    <span class="hljs-comment">// and sends them to the channel.</span>
    in := generate(<span class="hljs-number">100</span>, <span class="hljs-number">3</span>)

    <span class="hljs-keyword">var</span> wg sync.WaitGroup
    wg.Add(<span class="hljs-number">2</span>)

    <span class="hljs-comment">// count reads words from the input channel</span>
    <span class="hljs-comment">// and counts how often each one appears.</span>
    count := <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(counters []<span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]<span class="hljs-keyword">int</span>, idx <span class="hljs-keyword">int</span>)</span></span> {
        <span class="hljs-keyword">defer</span> wg.Done()
        counter := <span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]<span class="hljs-keyword">int</span>{}
        <span class="hljs-keyword">for</span> word := <span class="hljs-keyword">range</span> in {
            counter[word]++
        }
        counters[idx] = counter
    }

    counters := <span class="hljs-built_in">make</span>([]<span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]<span class="hljs-keyword">int</span>, <span class="hljs-number">2</span>)
    <span class="hljs-keyword">go</span> count(counters, <span class="hljs-number">0</span>)
    <span class="hljs-keyword">go</span> count(counters, <span class="hljs-number">1</span>)
    wg.Wait()

    <span class="hljs-comment">// merge combines frequency maps.</span>
    counter := merge(counters...)
    fmt.Println(counter)
}

<span class="hljs-comment">// merge combines frequency maps into a single map.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">merge</span><span class="hljs-params">(counters ...<span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]<span class="hljs-keyword">int</span>)</span> <span class="hljs-title">map</span>[<span class="hljs-title">string</span>]<span class="hljs-title">int</span></span> {
    merged := <span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]<span class="hljs-keyword">int</span>{}
    <span class="hljs-keyword">for</span> _, counter := <span class="hljs-keyword">range</span> counters {
        <span class="hljs-keyword">for</span> word, freq := <span class="hljs-keyword">range</span> counter {
            merged[word] += freq
        }
    }
    <span class="hljs-keyword">return</span> merged
}
</code></pre>
<pre><code>map[cec:<span class="hljs-number">1</span> ... nol:<span class="hljs-number">2</span> not:<span class="hljs-number">3</span> ... tut:<span class="hljs-number">1</span>]
</code></pre><p>Now each <code>count()</code> goroutine works with its own map, so there are no concurrent modifications. After all goroutines finish, <code>merge()</code> combines the results into a single map. This approach avoids data races, but it requires more memory and an extra merge step.</p>
<hr />
<h2 id="heading-mutex">Mutex</h2>
<p>Sometimes you can't avoid concurrent modifications. In such cases, you need to synchronize access to shared data. A <em>mutex</em> (short for "mutual exclusion") is a synchronization primitive that ensures only one goroutine can access a piece of code at a time.</p>
<p>Let's fix our word frequency program using a mutex:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-comment">// generate creates 100 words, each 3 letters long,</span>
    <span class="hljs-comment">// and sends them to the channel.</span>
    in := generate(<span class="hljs-number">100</span>, <span class="hljs-number">3</span>)

    <span class="hljs-keyword">var</span> wg sync.WaitGroup
    wg.Add(<span class="hljs-number">2</span>)

    <span class="hljs-comment">// count reads words from the input channel</span>
    <span class="hljs-comment">// and counts how often each one appears.</span>
    count := <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(lock *sync.Mutex, counter <span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]<span class="hljs-keyword">int</span>)</span></span> {
        <span class="hljs-keyword">defer</span> wg.Done()
        <span class="hljs-keyword">for</span> word := <span class="hljs-keyword">range</span> in {
            lock.Lock()       <span class="hljs-comment">// (2)</span>
            counter[word]++
            lock.Unlock()     <span class="hljs-comment">// (3)</span>
        }
    }

    <span class="hljs-keyword">var</span> lock sync.Mutex       <span class="hljs-comment">// (1)</span>
    counter := <span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]<span class="hljs-keyword">int</span>{}
    <span class="hljs-keyword">go</span> count(&amp;lock, counter)
    <span class="hljs-keyword">go</span> count(&amp;lock, counter)
    wg.Wait()

    fmt.Println(counter)
}
</code></pre>
<pre><code>map[cec:<span class="hljs-number">1</span> ... nol:<span class="hljs-number">2</span> not:<span class="hljs-number">3</span> ... tut:<span class="hljs-number">1</span>]
</code></pre><p>We created the <code>lock</code> mutex ➊ and used it to protect access to the shared <code>counter</code> map ➋ ➌. This way, the <code>count()</code> goroutines don't cause data races, and the final <code>counter[word]</code> value is correct.</p>
<p>Here's how a mutex works:</p>
<ul>
<li><code>Lock()</code> acquires the mutex. If another goroutine already has it, <code>Lock()</code> blocks until the mutex becomes available.</li>
<li><code>Unlock()</code> releases the mutex, allowing other goroutines to acquire it.</li>
</ul>
<p>The code between <code>Lock()</code> and <code>Unlock()</code> is called a <em>critical section</em>. Only one goroutine can execute the critical section at a time.</p>
<p><strong>Important notes about mutexes:</strong></p>
<ul>
<li>Always unlock a mutex after locking it. Use <code>defer</code> to ensure unlocking happens even if the code panics.</li>
<li>Don't lock a mutex twice in the same goroutine without unlocking it first — this will cause a deadlock. Go's mutexes are not reentrant. This makes things harder for people who like to use mutexes in recursive functions (which isn't a great idea anyway).</li>
<li>Like a wait group, a mutex has internal state, so you should only pass it as a pointer.</li>
</ul>
<hr />
<h2 id="heading-read-write-mutex">Read-Write Mutex</h2>
<p>A regular mutex doesn't distinguish between read and write access: if one goroutine locks the mutex, others can't access the protected code. This isn't always necessary.</p>
<p>Here's the situation:</p>
<ul>
<li>One <code>writer</code> goroutine writes data.</li>
<li>Four <code>reader</code> goroutines read that same data.</li>
</ul>
<pre><code class="lang-go"><span class="hljs-keyword">var</span> wg sync.WaitGroup
wg.Add(<span class="hljs-number">5</span>)

<span class="hljs-keyword">var</span> lock sync.Mutex

<span class="hljs-comment">// writer fills in the word frequency map.</span>
writer := <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(counter <span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]<span class="hljs-keyword">int</span>, nWrites <span class="hljs-keyword">int</span>)</span></span> {
    <span class="hljs-keyword">defer</span> wg.Done()
    <span class="hljs-keyword">for</span> ; nWrites &gt; <span class="hljs-number">0</span>; nWrites-- {
        word := randomWord(<span class="hljs-number">3</span>)
        lock.Lock()
        counter[word]++
        time.Sleep(time.Millisecond)
        lock.Unlock()
    }
}

<span class="hljs-comment">// reader looks up random words in the frequency map.</span>
reader := <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(counter <span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]<span class="hljs-keyword">int</span>, nReads <span class="hljs-keyword">int</span>)</span></span> {
    <span class="hljs-keyword">defer</span> wg.Done()
    <span class="hljs-keyword">for</span> ; nReads &gt; <span class="hljs-number">0</span>; nReads-- {
        word := randomWord(<span class="hljs-number">3</span>)
        lock.Lock()
        _ = counter[word]
        time.Sleep(time.Millisecond)
        lock.Unlock()
    }
}

start := time.Now()

counter := <span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]<span class="hljs-keyword">int</span>{}
<span class="hljs-keyword">go</span> writer(counter, <span class="hljs-number">100</span>)
<span class="hljs-keyword">go</span> reader(counter, <span class="hljs-number">100</span>)
<span class="hljs-keyword">go</span> reader(counter, <span class="hljs-number">100</span>)
<span class="hljs-keyword">go</span> reader(counter, <span class="hljs-number">100</span>)
<span class="hljs-keyword">go</span> reader(counter, <span class="hljs-number">100</span>)
wg.Wait()

fmt.Println(<span class="hljs-string">"Took"</span>, time.Since(start))
</code></pre>
<pre><code>Took <span class="hljs-number">500</span>ms
</code></pre><p>Even though we started 4 reader goroutines, they run sequentially because of the mutex. This isn't really necessary. It makes sense for readers to wait while the writer is updating the map. But why can't the readers run in parallel? They're not changing any data.</p>
<p>The <code>sync</code> package includes a <code>sync.RWMutex</code> that separates readers and writers. It provides two sets of methods:</p>
<ul>
<li><code>Lock</code> / <code>Unlock</code> lock and unlock the mutex for both reading and writing.</li>
<li><code>RLock</code> / <code>RUnlock</code> lock and unlock the mutex for reading only.</li>
</ul>
<p>Here's how it works:</p>
<ul>
<li>If a goroutine locks the mutex with <code>Lock()</code>, other goroutines will be blocked if they try to use <code>Lock()</code> or <code>RLock()</code>.</li>
<li>If a goroutine locks the mutex with <code>RLock()</code>, other goroutines can also lock it with <code>RLock()</code> without being blocked.</li>
<li>If at least one goroutine has locked the mutex with <code>RLock()</code>, other goroutines will be blocked if they try to use <code>Lock()</code>.</li>
</ul>
<p>This creates a "single writer, multiple readers" setup. Let's verify it:</p>
<pre><code class="lang-go"><span class="hljs-keyword">var</span> wg sync.WaitGroup
wg.Add(<span class="hljs-number">5</span>)

<span class="hljs-keyword">var</span> lock sync.RWMutex          <span class="hljs-comment">// (1)</span>

<span class="hljs-comment">// writer fills in the word frequency map.</span>
writer := <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(counter <span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]<span class="hljs-keyword">int</span>, nWrites <span class="hljs-keyword">int</span>)</span></span> {
    <span class="hljs-comment">// Not changed.</span>
    <span class="hljs-keyword">defer</span> wg.Done()
    <span class="hljs-keyword">for</span> ; nWrites &gt; <span class="hljs-number">0</span>; nWrites-- {
        word := randomWord(<span class="hljs-number">3</span>)
        lock.Lock()
        counter[word]++
        time.Sleep(time.Millisecond)
        lock.Unlock()
    }
}

<span class="hljs-comment">// reader looks up random words in the frequency map.</span>
reader := <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(counter <span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]<span class="hljs-keyword">int</span>, nReads <span class="hljs-keyword">int</span>)</span></span> {
    <span class="hljs-keyword">defer</span> wg.Done()
    <span class="hljs-keyword">for</span> ; nReads &gt; <span class="hljs-number">0</span>; nReads-- {
        word := randomWord(<span class="hljs-number">3</span>)
        lock.RLock()           <span class="hljs-comment">// (2)</span>
        _ = counter[word]
        time.Sleep(time.Millisecond)
        lock.RUnlock()         <span class="hljs-comment">// (3)</span>
    }
}

start := time.Now()

counter := <span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]<span class="hljs-keyword">int</span>{}
<span class="hljs-keyword">go</span> writer(counter, <span class="hljs-number">100</span>)
<span class="hljs-keyword">go</span> reader(counter, <span class="hljs-number">100</span>)
<span class="hljs-keyword">go</span> reader(counter, <span class="hljs-number">100</span>)
<span class="hljs-keyword">go</span> reader(counter, <span class="hljs-number">100</span>)
<span class="hljs-keyword">go</span> reader(counter, <span class="hljs-number">100</span>)
wg.Wait()

fmt.Println(<span class="hljs-string">"Took"</span>, time.Since(start))
</code></pre>
<pre><code>Took <span class="hljs-number">200</span>ms
</code></pre><p>The mutex type ➊ has changed, so have the locking ➋ and unlocking ➌ methods in the reader. Now, readers run concurrently, but they always wait while the writer updates the map. That's exactly what we need!</p>
<hr />
<h2 id="heading-channel-as-mutex">Channel as Mutex</h2>
<p>Let's go back to the program that counts word frequencies:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-comment">// generate creates 100 words, each 3 letters long,</span>
    <span class="hljs-comment">// and sends them to the channel.</span>
    in := generate(<span class="hljs-number">100</span>, <span class="hljs-number">3</span>)

    <span class="hljs-keyword">var</span> wg sync.WaitGroup
    wg.Add(<span class="hljs-number">2</span>)

    <span class="hljs-comment">// count reads words from the input channel</span>
    <span class="hljs-comment">// and counts how often each one appears.</span>
    count := <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(lock *sync.Mutex, counter <span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]<span class="hljs-keyword">int</span>)</span></span> {
        <span class="hljs-keyword">defer</span> wg.Done()
        <span class="hljs-keyword">for</span> word := <span class="hljs-keyword">range</span> in {
            lock.Lock()       <span class="hljs-comment">// (2)</span>
            counter[word]++
            lock.Unlock()     <span class="hljs-comment">// (3)</span>
        }
    }

    <span class="hljs-keyword">var</span> lock sync.Mutex       <span class="hljs-comment">// (1)</span>
    counter := <span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]<span class="hljs-keyword">int</span>{}
    <span class="hljs-keyword">go</span> count(&amp;lock, counter)
    <span class="hljs-keyword">go</span> count(&amp;lock, counter)
    wg.Wait()

    fmt.Println(counter)
}
</code></pre>
<p>We created the <code>lock</code> mutex ➊ and used it to protect access to the shared <code>counter</code> map ➋ ➌. This way, the <code>count()</code> goroutines don't cause data races, and the final <code>counter[word]</code> value is correct.</p>
<p>We can also use a channel instead of a mutex to protect shared data:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> token <span class="hljs-keyword">struct</span>{}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-comment">// generate creates 100 words, each 3 letters long,</span>
    <span class="hljs-comment">// and sends them to the channel.</span>
    in := generate(<span class="hljs-number">100</span>, <span class="hljs-number">3</span>)

    <span class="hljs-keyword">var</span> wg sync.WaitGroup
    wg.Add(<span class="hljs-number">2</span>)

    <span class="hljs-comment">// count reads words from the input channel</span>
    <span class="hljs-comment">// and counts how often each one appears.</span>
    count := <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(lock <span class="hljs-keyword">chan</span> token, counter <span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]<span class="hljs-keyword">int</span>)</span></span> {
        <span class="hljs-keyword">defer</span> wg.Done()
        <span class="hljs-keyword">for</span> word := <span class="hljs-keyword">range</span> in {
            lock &lt;- token{}     <span class="hljs-comment">// (2)</span>
            counter[word]++
            &lt;-lock              <span class="hljs-comment">// (3)</span>
        }
    }

    lock := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> token, <span class="hljs-number">1</span>) <span class="hljs-comment">// (1)</span>

    counter := <span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]<span class="hljs-keyword">int</span>{}
    <span class="hljs-keyword">go</span> count(lock, counter)
    <span class="hljs-keyword">go</span> count(lock, counter)
    wg.Wait()

    fmt.Println(counter)
}
</code></pre>
<pre><code>map[cec:<span class="hljs-number">1</span> ... nol:<span class="hljs-number">2</span> not:<span class="hljs-number">3</span> ... tut:<span class="hljs-number">1</span>]
</code></pre><p>We created a <code>lock</code> channel with a one-element buffer ➊ and used it to protect access to the shared <code>counter</code> map ➋ ➌.</p>
<p>Two <code>count()</code> goroutines run concurrently. However, in each loop iteration, only one of them can put a token into the <code>lock</code> channel (like locking a mutex), update the counter, and take the token back out (like unlocking a mutex). So, even though the goroutines run in parallel, changes to the map happen sequentially.</p>
<p>As a result, the <code>count()</code> goroutines don't cause data races, and the final <code>counter[word]</code> value is correct — just like when we used a mutex.</p>
<p>Go's channels are a versatile concurrency tool. Often, you can use a channel instead of lower-level synchronization primitives. Sometimes, using a channel is unnecessary, as in the example above. Other times, however, it makes your code simpler and helps prevent mistakes. You'll see this idea come up again throughout the book.</p>
<hr />
<h2 id="heading-summary">Summary</h2>
<p>Now you know how to safely change shared data from multiple goroutines using mutexes. Be careful not to overuse them — it's easy to make mistakes and cause data races or deadlocks.</p>
<p>Key points to remember:</p>
<ul>
<li><strong>Data races occur</strong> when multiple goroutines access the same variable concurrently, and at least one modifies it.</li>
<li><strong>Use the race detector</strong> (<code>go run -race</code>) to detect data races in your code.</li>
<li><strong>Avoid concurrent modifications</strong> when possible by using separate data structures per goroutine.</li>
<li><strong>Use mutexes</strong> to protect shared data when concurrent modifications are unavoidable.</li>
<li><strong>Consider RWMutex</strong> when you have multiple readers and fewer writers.</li>
<li><strong>Channels can serve as mutexes</strong> in some scenarios, though mutexes are usually more appropriate.</li>
</ul>
<p>Safe concurrent programming requires careful attention to data access patterns and proper synchronization.</p>
]]></content:encoded></item><item><title><![CDATA[Distributed Services vs. Unified Applications: Striking the Perfect Balance]]></title><description><![CDATA[Distributed Services vs. Unified Applications: Striking the Perfect Balance
Hot take: You don't have a microservice architecture, you have a distributed monolith with trust issues.
In the rush to "go micro," many teams end up slicing their systems in...]]></description><link>https://blog.fshtab.com/microservices-vs-monoliths-finding-the-right-balance</link><guid isPermaLink="true">https://blog.fshtab.com/microservices-vs-monoliths-finding-the-right-balance</guid><dc:creator><![CDATA[Fedor S]]></dc:creator><pubDate>Fri, 22 Mar 2024 13:45:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1766406500313/a725ee7b-b1fe-497e-88ec-29607508f9de.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-distributed-services-vs-unified-applications-striking-the-perfect-balance">Distributed Services vs. Unified Applications: Striking the Perfect Balance</h1>
<p>Hot take: You don't have a microservice architecture, you have a distributed monolith with trust issues.</p>
<p>In the rush to "go micro," many teams end up slicing their systems into tens of tiny, chatty services that spend more time talking to each other than doing any real work. Every API call adds latency. <strong>Every dependency adds failure points</strong>. Every "independent" deployment ends up blocked by another team's version bump.</p>
<p>Sound familiar? 🙂</p>
<p>The pain you're feeling isn't the cost of scale, it's the cost of premature, arbitrary decomposition.</p>
<hr />
<h2 id="heading-the-microservices-trap">The Microservices Trap</h2>
<p><strong>How we got here:</strong><br />It starts innocently enough. You read about Netflix's architecture. You attended some random conference, you read some articles online. Someone mentions "Conway's Law" in a late Friday meeting. Suddenly, the mandate comes down: "We're going microservices."</p>
<p>Within six months, you have:</p>
<ul>
<li>A user service</li>
<li>An auth service</li>
<li>A notification service</li>
<li>An email service (because notifications and emails are totally different domains)</li>
<li>A logging service</li>
<li>A metrics service</li>
<li>A service that just... creates uuids?</li>
</ul>
<p>Each one has its own:</p>
<ul>
<li>Repository</li>
<li>CI/CD pipeline</li>
<li>Database</li>
<li>Deployment schedule</li>
<li>API versioning scheme</li>
<li>Team ownership</li>
</ul>
<p><strong>The reality check:</strong><br />To fetch a user's profile, you now make 7 API calls across 4 services. Your p99 latency is 800ms. Your error budget is constantly exceeded because something is always down. Your observability costs more than your compute.<br /><strong>You've achieved distributed monolith status.</strong></p>
<hr />
<h2 id="heading-the-hidden-costs-nobody-talks-about">The Hidden Costs Nobody Talks About</h2>
<p><strong>1. Network is not free</strong></p>
<p>Monolith: function call = 0.001ms<br />Microservice: HTTP call = 5-50ms (plus serialization, auth, retries...)</p>
<p>When your checkout flow hits 12 services, that's <strong>60-600ms of network overhead</strong> before you've done any real work.</p>
<p>And that's assuming everything works. Add retries, circuit breakers, and cascading failures, and you're looking at seconds, not milliseconds.</p>
<p><strong>2. Distributed debugging is a nightmare</strong></p>
<p>Bug report: "User can't complete checkout."</p>
<p>In a monolith:</p>
<ul>
<li>Check the logs</li>
<li>Set a breakpoint</li>
<li>Find the issue</li>
<li>Fix it</li>
<li>Deploy</li>
</ul>
<p>In microservices:</p>
<ul>
<li>Which service failed?</li>
<li>Check distributed traces (if they exist)</li>
<li>Correlate logs across 6 services</li>
<li>Find the issue is a timeout in service D caused by a memory leak in service B triggered by bad data from service A</li>
<li>Coordinate deployments across 3 teams</li>
<li>Hope you didn't introduce new bugs</li>
</ul>
<p><strong>3. "Independent" deployments aren't independent</strong></p>
<p>Your user-service runs on SQLAlchemy 1.4. The payments team just upgraded their shared models package to SQLAlchemy 2.0 for "better async support." Now your queries throw deprecation warnings everywhere and half your tests fail.</p>
<hr />
<h2 id="heading-when-microservices-actually-make-sense">When Microservices Actually Make Sense</h2>
<p>Don't get me wrong, microservices <strong>can be the right choice</strong>. But they're an optimization for specific problems, not a default architecture pattern.</p>
<p><strong>When done right, microservices unlock real organizational power.</strong></p>
<p>They let large teams ship features <strong>independently</strong>, scale bottlenecks in <strong>isolation</strong>, and mix technologies to fit <strong>different workloads</strong>. You can deploy a single service without freezing the entire platform. You can experiment faster, fail safely, and iterate without merge conflicts across 50 engineers.</p>
<p>For truly global-scale systems, think payments, logistics, or media streaming, microservices let you scale the right parts independently. Instead of scaling the whole app just because one endpoint gets hammered, you scale that service and <strong>keep costs predictable</strong>.</p>
<p>They also make it easier to enforce <strong>clear domain ownership</strong>. Each team owns their service, their schema, and their roadmap, which reduces cross-team dependency chaos when you're big enough to need it.</p>
<p><strong>Good reasons to split services:</strong></p>
<p><strong>1. Genuine scale differences</strong>  </p>
<pre><code>Example: Your image processing pipeline handles <span class="hljs-number">10</span>K requests/sec
         Your admin panel handles <span class="hljs-number">10</span> requests/sec
</code></pre><p>These shouldn't share resources. Split them.</p>
<p><strong>2. Team autonomy at real scale</strong><br />If you have 50+ engineers stepping on each other's toes in the same codebase, and you've already tried modularization, <em>then</em> consider splitting.</p>
<p><strong>3. Technology constraints</strong><br />You need Python's ML libraries for recommendations but Go's performance for your API gateway. Fair enough.</p>
<p><strong>4. Actual domain boundaries</strong><br />Payments and product catalogs are genuinely different domains with different business rules, compliance requirements, and failure modes. They can evolve independently.</p>
<hr />
<h2 id="heading-the-monolith-advantage-that-nobody-admits">The Monolith Advantage (That Nobody Admits)</h2>
<p>A well-structured monolith gives you:</p>
<p><strong>Simplicity:</strong></p>
<ul>
<li>One codebase to understand</li>
<li>One deployment pipeline</li>
<li>One database transaction (ACID guarantees for free!)</li>
<li>One place to search for code</li>
<li>One set of dependencies to manage</li>
</ul>
<p><strong>Performance:</strong></p>
<ul>
<li>In memory function calls, not HTTP</li>
<li>No serialization overhead</li>
<li>No network failures</li>
<li>Shared caches actually work</li>
</ul>
<p><strong>Developer experience:</strong></p>
<ul>
<li>Run the entire app locally</li>
<li>Debugger actually works</li>
<li>Tests run fast</li>
<li>Refactoring is safe</li>
</ul>
<p><strong>"But monoliths don't scale!"</strong></p>
<p><strong>Wrong.</strong> Shopify runs on a Rails monolith and handles Black Friday traffic. GitHub's monolith serves millions of developers. Stack Overflow famously runs on a handful of servers.</p>
<p>You scale a monolith by:</p>
<ol>
<li>Vertical scaling (modern instances are HUGE)</li>
<li>Horizontal scaling (stateless apps scale fine)</li>
<li>Strategic caching</li>
<li>Database optimization</li>
</ol>
<hr />
<h2 id="heading-the-middle-path-what-you-should-actually-do">The Middle Path (What You Should Actually Do)</h2>
<p>Here's the nuance nobody talks about: <strong>You don't choose between monolith and microservices. You choose when to split.</strong></p>
<p><strong>Start with a modular monolith: This isn't just about folders, it's about Bounded Contexts.</strong>  </p>
<pre><code>app/
├── modules/
│   ├── users/
│   │   ├── domain/
│   │   ├── api/
│   │   └── repository/
│   ├── payments/
│   │   └── ...
│   └── inventory/
│       └── ...
</code></pre><p>Good modules have:</p>
<ul>
<li><strong>Clear interfaces</strong> (defined contracts between modules)</li>
<li><strong>Weak coupling</strong> (changes in one don't ripple to others)</li>
<li><strong>Strong cohesion</strong> (related logic lives together)</li>
</ul>
<p><strong>When to extract a service:</strong></p>
<p>You have data that justifies the split:</p>
<ul>
<li>This module is causing 80% of deploys</li>
<li>This team is blocked waiting for other teams</li>
<li>This component needs different scaling characteristics</li>
<li>This domain has genuinely independent lifecycle</li>
</ul>
<p><strong>The extraction looks like:</strong>  </p>
<pre><code>Monolith → Modular Monolith → <span class="hljs-number">3</span> well-defined services → Scale what needs it
</code></pre><p>Not:</p>
<p>Monolith → 47 microservices → ??? → Black Magic</p>
<hr />
<h2 id="heading-red-flags-youve-gone-too-micro">Red Flags You've Gone Too Micro 🚩</h2>
<p>You might have a problem if:</p>
<ul>
<li>Your services call each other in chains<br />If request flow looks like: A → B → C → D → B → E, you've just built a distributed ball of mud.</li>
<li>You can't add a feature without touching 5+ services<br />That's not independence, that's tight coupling with extra steps.</li>
<li>Your team spends more time on infrastructure than features<br />Kubernetes, service mesh, distributed tracing... these are costs, not features.</li>
<li>Simple changes require "cross-team coordination meetings"<br />You've replaced code dependencies with human dependencies. That's slower.</li>
<li>Your error messages look like:<br /><code>"Service timeout in payment-gateway calling order-validator calling inventory-checker calling warehouse api"</code></li>
</ul>
<hr />
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>The truth is: microservices aren't a magic scalability pill, they're an organizational tool.<br />If your team isn't struggling with coordination or monolith scaling yet, breaking things apart just creates complexity without benefit.<br />The real skill isn't in cutting your system into tiny pieces, it's knowing where to draw the lines. Strong service boundaries come from domain understanding, not arbitrary code size.</p>
<p>So before you spin up service number 47, ask yourself:</p>
<p>"Is this solving a scaling problem, or just creating a communication problem?"</p>
<p><strong>Sometimes the best architecture decision is the one you don't make.</strong></p>
<p>--<br />What's your take? Are you running a microservices architecture or a distributed monolith? Let me know in the comments, I'd love to hear your war stories.</p>
]]></content:encoded></item><item><title><![CDATA[Go: Goroutines and Concurrent Programming]]></title><description><![CDATA[Эта статья представляет собой введение в конкурентное программирование на Go через практические примеры. Давайте сразу перейдем к написанию конкурентной программы!

Goroutines
Предположим, у нас есть функция, которая произносит фразу слово за словом ...]]></description><link>https://blog.fshtab.com/go-goroutines-concurrency</link><guid isPermaLink="true">https://blog.fshtab.com/go-goroutines-concurrency</guid><dc:creator><![CDATA[Fedor S]]></dc:creator><pubDate>Thu, 07 Mar 2024 09:15:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1766407828358/b728076e-4a53-4ade-92fa-10f85daee185.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Эта статья представляет собой введение в конкурентное программирование на Go через практические примеры. Давайте сразу перейдем к написанию конкурентной программы!</p>
<hr />
<h2 id="heading-goroutines">Goroutines</h2>
<p>Предположим, у нас есть функция, которая произносит фразу слово за словом с паузами:</p>
<pre><code class="lang-go"><span class="hljs-comment">// say выводит каждое слово фразы.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">say</span><span class="hljs-params">(phrase <span class="hljs-keyword">string</span>)</span></span> {
    <span class="hljs-keyword">for</span> _, word := <span class="hljs-keyword">range</span> strings.Fields(phrase) {
        fmt.Printf(<span class="hljs-string">"Simon says: %s...\n"</span>, word)
        dur := time.Duration(rand.Intn(<span class="hljs-number">100</span>)) * time.Millisecond
        time.Sleep(dur)
    }
}
</code></pre>
<p>Вызовем её из функции main:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    say(<span class="hljs-string">"go is awesome"</span>)
}
</code></pre>
<p>Теперь создадим двух говорящих, каждый со своей фразой:</p>
<pre><code class="lang-go"><span class="hljs-comment">// say выводит каждое слово фразы.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">say</span><span class="hljs-params">(id <span class="hljs-keyword">int</span>, phrase <span class="hljs-keyword">string</span>)</span></span> {
    <span class="hljs-keyword">for</span> _, word := <span class="hljs-keyword">range</span> strings.Fields(phrase) {
        fmt.Printf(<span class="hljs-string">"Worker #%d says: %s...\n"</span>, id, word)
        dur := time.Duration(rand.Intn(<span class="hljs-number">100</span>)) * time.Millisecond
        time.Sleep(dur)
    }
}
</code></pre>
<p>Запустим программу:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    say(<span class="hljs-number">1</span>, <span class="hljs-string">"go is awesome"</span>)
    say(<span class="hljs-number">2</span>, <span class="hljs-string">"cats are cute"</span>)
}
</code></pre>
<p>Функции выполняются последовательно. Чтобы они говорили одновременно, добавим <code>go</code> перед вызовом функции <code>say()</code>:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">go</span> say(<span class="hljs-number">1</span>, <span class="hljs-string">"go is awesome"</span>)
    <span class="hljs-keyword">go</span> say(<span class="hljs-number">2</span>, <span class="hljs-string">"cats are cute"</span>)
    time.Sleep(<span class="hljs-number">500</span> * time.Millisecond)
}
</code></pre>
<p>Теперь они действительно соревнуются за наше внимание! Когда мы пишем <code>go f()</code>, функция <code>f()</code> выполняется независимо от остальных.</p>
<p>Если вы знакомы с конкурентностью в Python, JavaScript или других языках с async/await, не пытайтесь применить этот опыт к Go. Go использует совершенно другой подход к конкурентности. Попробуйте взглянуть на это свежим взглядом.</p>
<p>Функции, запускаемые с <code>go</code>, называются <strong>goroutines</strong>. Среда выполнения Go управляет этими goroutines и распределяет их между потоками операционной системы, работающими на ядрах CPU. По сравнению с потоками ОС, goroutines легковесны, поэтому вы можете создавать сотни или тысячи их.</p>
<p>Вы можете задаться вопросом, зачем нам нужен <code>time.Sleep()</code> в функции main. Давайте это проясним.</p>
<hr />
<h2 id="heading-zavisimye-i-nezavisimye-goroutines">Зависимые и независимые goroutines</h2>
<p>Goroutines полностью независимы. Когда мы вызываем <code>go say()</code>, функция выполняется сама по себе. <code>main</code> не ждёт её завершения. Поэтому если мы напишем main так:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">go</span> say(<span class="hljs-number">1</span>, <span class="hljs-string">"go is awesome"</span>)
    <span class="hljs-keyword">go</span> say(<span class="hljs-number">2</span>, <span class="hljs-string">"cats are cute"</span>)
}
</code></pre>
<p>— программа ничего не выведет. <code>main</code> завершается раньше, чем наши goroutines успевают что-то сказать, и поскольку main завершена, вся программа завершается.</p>
<p>Функция <code>main</code> также является goroutine, но она запускается неявно при старте программы. Таким образом, у нас есть три goroutines: <code>main</code>, <code>say(1)</code> и <code>say(2)</code>, все они независимы. Единственная особенность в том, что когда <code>main</code> завершается, всё остальное тоже завершается.</p>
<h3 id="heading-waitgroup">WaitGroup</h3>
<p>Использование <code>time.Sleep()</code> для ожидания goroutines — плохая идея, потому что мы не можем предсказать, сколько времени они займут. Лучший подход — использовать wait group:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">var</span> wg sync.WaitGroup <span class="hljs-comment">// (1)</span>

    wg.Add(<span class="hljs-number">1</span>)             <span class="hljs-comment">// (2)</span>
    <span class="hljs-keyword">go</span> say(&amp;wg, <span class="hljs-number">1</span>, <span class="hljs-string">"go is awesome"</span>)

    wg.Add(<span class="hljs-number">1</span>)             <span class="hljs-comment">// (2)</span>
    <span class="hljs-keyword">go</span> say(&amp;wg, <span class="hljs-number">2</span>, <span class="hljs-string">"cats are cute"</span>)

    wg.Wait()             <span class="hljs-comment">// (3)</span>
}

<span class="hljs-comment">// say выводит каждое слово фразы.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">say</span><span class="hljs-params">(wg *sync.WaitGroup, id <span class="hljs-keyword">int</span>, phrase <span class="hljs-keyword">string</span>)</span></span> {
    <span class="hljs-keyword">for</span> _, word := <span class="hljs-keyword">range</span> strings.Fields(phrase) {
        fmt.Printf(<span class="hljs-string">"Worker #%d says: %s...\n"</span>, id, word)
        dur := time.Duration(rand.Intn(<span class="hljs-number">100</span>)) * time.Millisecond
        time.Sleep(dur)
    }
    wg.Done()             <span class="hljs-comment">// (4)</span>
}
</code></pre>
<p><code>wg</code> ➊ имеет внутри счётчик. Вызов <code>wg.Add(1)</code> ➋ увеличивает его на единицу, а <code>wg.Done()</code> ➍ уменьшает. <code>wg.Wait()</code> ➌ блокирует goroutine (в данном случае <code>main</code>) до тех пор, пока счётчик не достигнет нуля. Таким образом, <code>main</code> ждёт завершения <code>say(1)</code> и <code>say(2)</code> перед выходом.</p>
<p>Однако этот подход смешивает бизнес-логику (<code>say</code>) с логикой конкурентности (<code>wg</code>). В результате мы не можем легко запустить <code>say</code> в обычном, неконкурентном коде.</p>
<p>В Go принято разделять логику конкурентности и бизнес-логику. Обычно это делается с помощью отдельных функций. В простых случаях, как у нас, подойдут даже анонимные функции:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">var</span> wg sync.WaitGroup
    wg.Add(<span class="hljs-number">2</span>)

    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">defer</span> wg.Done()
        say(<span class="hljs-number">1</span>, <span class="hljs-string">"go is awesome"</span>)
    }()

    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">defer</span> wg.Done()
        say(<span class="hljs-number">2</span>, <span class="hljs-string">"cats are cute"</span>)
    }()

    wg.Wait()
}
</code></pre>
<p>Вот как это работает:</p>
<ul>
<li><p>Мы знаем, что будет две goroutines, поэтому сразу вызываем <code>wg.Add(2)</code>. Альтернативно, мы можем вызывать <code>wg.Add(1)</code> перед запуском каждой goroutine — результат будет тот же.</p>
</li>
<li><p>Анонимные функции запускаются с <code>go</code> так же, как и обычные.</p>
</li>
<li><p><code>defer wg.Done()</code> гарантирует, что goroutine уменьшит счётчик перед выходом, даже если <code>say</code> вызовет панику.</p>
</li>
<li><p>Сама <code>say</code> ничего не знает о конкурентности и просто работает.</p>
</li>
</ul>
<h3 id="heading-waitgroupgo">WaitGroup.Go</h3>
<p>Метод <code>WaitGroup.Go</code> (Go 1.25+) автоматически увеличивает счётчик wait group, запускает функцию в goroutine и уменьшает счётчик, когда она завершается. Это означает, что мы можем переписать пример выше без использования <code>wg.Add()</code> и <code>wg.Done()</code>:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">var</span> wg sync.WaitGroup

    wg.Go(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        fmt.Println(<span class="hljs-string">"go is awesome"</span>)
    })

    wg.Go(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        fmt.Println(<span class="hljs-string">"cats are cute"</span>)
    })

    wg.Wait()
    fmt.Println(<span class="hljs-string">"done"</span>)
}
</code></pre>
<p>Реализация использует <code>Add</code> и <code>Done</code> так же, как мы делали раньше:</p>
<pre><code class="lang-go"><span class="hljs-comment">// https://github.com/golang/go/blob/master/src/sync/waitgroup.go</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(wg *WaitGroup)</span> <span class="hljs-title">Go</span><span class="hljs-params">(f <span class="hljs-keyword">func</span>()</span>)</span> {
    wg.Add(<span class="hljs-number">1</span>)
    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">defer</span> wg.Done()
        f()
    }()
}
</code></pre>
<hr />
<h2 id="heading-channels">Channels</h2>
<p>Запуск множества goroutines — это здорово, но как они обмениваются данными? В Go goroutines могут передавать значения друг другу через <strong>channels</strong>. Канал — это как окно, через которое одна goroutine может что-то бросить, а другая — поймать.</p>
<pre><code class="lang-plaintext">┌─────────────┐    ┌─────────────┐
│ goroutine A │    │ goroutine B │
│             └────┘             │
│        X &lt;-  chan  &lt;- X        │
│             ┌────┐             │
│             │    │             │
└─────────────┘    └─────────────┘
</code></pre>
<p>Goroutine B отправляет значение X в goroutine A.</p>
<p>Вот как это работает:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-comment">// Чтобы создать канал, используйте `make(chan type)`.</span>
    <span class="hljs-comment">// Канал может принимать только значения указанного типа:</span>
    messages := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">string</span>)

    <span class="hljs-comment">// Чтобы отправить значение в канал,</span>
    <span class="hljs-comment">// используйте синтаксис `channel &lt;-`.</span>
    <span class="hljs-comment">// Отправим "ping":</span>
    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> { messages &lt;- <span class="hljs-string">"ping"</span> }()

    <span class="hljs-comment">// Чтобы получить значение из канала,</span>
    <span class="hljs-comment">// используйте синтаксис `&lt;-channel`.</span>
    <span class="hljs-comment">// Получим "ping" и выведем его:</span>
    msg := &lt;-messages
    fmt.Println(msg)
}
</code></pre>
<p>Когда программа выполняется, первая goroutine (анонимная) отправляет сообщение второй (<code>main</code>) через канал <code>messages</code>.</p>
<p>Отправка значения через канал — это синхронная операция. Когда отправляющая goroutine записывает значение в канал (<code>messages &lt;- "ping"</code>), она блокируется и ждёт, пока кто-то получит это значение (<code>&lt;-messages</code>). Только после этого она продолжает работу:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    messages := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">string</span>)

    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        fmt.Println(<span class="hljs-string">"B: Sending message..."</span>)
        messages &lt;- <span class="hljs-string">"ping"</span>                    <span class="hljs-comment">// (1)</span>
        fmt.Println(<span class="hljs-string">"B: Message sent!"</span>)       <span class="hljs-comment">// (2)</span>
    }()

    fmt.Println(<span class="hljs-string">"A: Doing some work..."</span>)
    time.Sleep(<span class="hljs-number">500</span> * time.Millisecond)
    fmt.Println(<span class="hljs-string">"A: Ready to receive a message..."</span>)

    &lt;-messages                               <span class="hljs-comment">//  (3)</span>

    fmt.Println(<span class="hljs-string">"A: Message received!"</span>)
    time.Sleep(<span class="hljs-number">100</span> * time.Millisecond)
}
</code></pre>
<p>После отправки сообщения в канал ➊ goroutine B блокируется. Только когда goroutine A получает сообщение ➌, goroutine B продолжает и выводит "message sent" ➋.</p>
<p>Таким образом, каналы не только передают данные, но и помогают синхронизировать независимые goroutines. Это пригодится позже.</p>
<hr />
<h2 id="heading-pattern-producer-consumer">Паттерн Producer-Consumer</h2>
<p>В программировании часто встречается паттерн "производитель-потребитель":</p>
<ul>
<li><p><strong>Производитель</strong> поставляет данные.</p>
</li>
<li><p><strong>Потребитель</strong> получает и обрабатывает их.</p>
</li>
</ul>
<p>В следующих примерах мы исследуем, как производители и потребители могут взаимодействовать через каналы.</p>
<p>Работаем с функцией, которая считает цифры в словах:</p>
<pre><code class="lang-go"><span class="hljs-comment">// counter хранит количество цифр в каждом слове.</span>
<span class="hljs-comment">// Ключ — слово, значение — количество цифр.</span>
<span class="hljs-keyword">type</span> counter <span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]<span class="hljs-keyword">int</span>

<span class="hljs-comment">// countDigitsInWords считает количество цифр</span>
<span class="hljs-comment">// в словах фразы.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">countDigitsInWords</span><span class="hljs-params">(phrase <span class="hljs-keyword">string</span>)</span> <span class="hljs-title">counter</span></span> {
    words := strings.Fields(phrase)
    <span class="hljs-comment">// ...</span>
    <span class="hljs-keyword">return</span> stats
}
</code></pre>
<h3 id="heading-rezultatnyj-kanal">Результатный канал</h3>
<p>Сделаем следующее:</p>
<ol>
<li><p>Запустим goroutine.</p>
</li>
<li><p>В этой goroutine пройдёмся по словам, посчитаем цифры в каждом и запишем в канал <code>counted</code> (производитель).</p>
</li>
<li><p>Во внешней функции читаем значения из канала и заполняем счётчик <code>stats</code> (потребитель).</p>
</li>
</ol>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">countDigitsInWords</span><span class="hljs-params">(phrase <span class="hljs-keyword">string</span>)</span> <span class="hljs-title">counter</span></span> {
    words := strings.Fields(phrase)
    counted := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>)

    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-comment">// Проходим по словам,</span>
        <span class="hljs-comment">// считаем количество цифр в каждом,</span>
        <span class="hljs-comment">// и записываем в канал counted.</span>
    }()

    <span class="hljs-comment">// Читаем значения из канала counted</span>
    <span class="hljs-comment">// и заполняем stats.</span>

    <span class="hljs-keyword">return</span> stats
}
</code></pre>
<hr />
<h2 id="heading-generator">Генератор</h2>
<p>До сих пор мы предполагали, что функция <code>countDigitsInWords</code> заранее знает все слова.</p>
<p>Но в реальной жизни мы не можем рассчитывать на такую роскошь. Данные могут приходить из базы данных или по сети, и функция не знает, сколько слов будет.</p>
<p>Давайте смоделируем эту ситуацию, передав функцию-генератор <code>next</code> вместо фразы. Каждый вызов <code>next()</code> даёт нам следующее слово из источника. Когда слов больше нет, она возвращает пустую строку.</p>
<p>Последовательная программа выглядела бы так:</p>
<pre><code class="lang-go"><span class="hljs-comment">// counter хранит количество цифр в каждом слове.</span>
<span class="hljs-comment">// Ключ — слово, значение — количество цифр.</span>
<span class="hljs-keyword">type</span> counter <span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]<span class="hljs-keyword">int</span>

<span class="hljs-comment">// countDigitsInWords считает количество цифр в словах,</span>
<span class="hljs-comment">// получая следующее слово через next().</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">countDigitsInWords</span><span class="hljs-params">(next <span class="hljs-keyword">func</span>()</span> <span class="hljs-title">string</span>) <span class="hljs-title">counter</span></span> {
    stats := counter{}

    <span class="hljs-keyword">for</span> {
        word := next()
        <span class="hljs-keyword">if</span> word == <span class="hljs-string">""</span> {
            <span class="hljs-keyword">break</span>
        }
        count := countDigits(word)
        stats[word] = count
    }

    <span class="hljs-keyword">return</span> stats
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    phrase := <span class="hljs-string">"0ne 1wo thr33 4068"</span>
    next := wordGenerator(phrase)
    stats := countDigitsInWords(next)
    printStats(stats)
}
</code></pre>
<p>Теперь добавим конкурентность.</p>
<h3 id="heading-generator-s-goroutines">Генератор с goroutines</h3>
<p>Если вы попытаетесь решить упражнение как предыдущее, столкнётесь с парой проблем:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">countDigitsInWords</span><span class="hljs-params">(next <span class="hljs-keyword">func</span>()</span> <span class="hljs-title">string</span>) <span class="hljs-title">counter</span></span> {
    counted := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>)

    <span class="hljs-comment">// считаем цифры в словах</span>
    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">for</span> {
            <span class="hljs-comment">// должна вернуться, когда</span>
            <span class="hljs-comment">// слов больше нет</span>
            word := next()
            count := countDigits(word)
            counted &lt;- count
        }
    }()

    <span class="hljs-comment">// заполняем stats словами</span>
    stats := counter{}
    <span class="hljs-keyword">for</span> {
        count := &lt;-counted
        <span class="hljs-comment">// как выйти из цикла,</span>
        <span class="hljs-comment">// когда слов больше нет?</span>
        <span class="hljs-keyword">if</span> ... {
            <span class="hljs-keyword">break</span>
        }
        <span class="hljs-comment">// откуда должно браться слово?</span>
        stats[...] = count
    }

    <span class="hljs-keyword">return</span> stats
}
</code></pre>
<p>Подумайте, что отправлять в канал <code>counted</code>, чтобы решить обе проблемы. Обратите внимание на тип пары.</p>
<hr />
<h2 id="heading-reader-i-worker">Reader и Worker</h2>
<p>Для более сложных задач полезно иметь goroutine для чтения данных (reader) и другую для обработки данных (worker). Используем этот подход в нашей функции:</p>
<pre><code class="lang-plaintext">┌───────────────┐               ┌───────────────┐
│ отправляет    │               │ считает цифры │               ┌────────────────┐
│ слова для     │ → (pending) → │ в словах     │ → (counted) → │ заполняет stats│
│ подсчёта      │               │              │               └────────────────┘
└───────────────┘               └───────────────┘
  reader           канал          worker            канал        внешняя функция
</code></pre>
<p>Сделаем следующее:</p>
<ol>
<li><p>Запустим goroutine, которая получает слова из генератора и отправляет их в канал <code>pending</code> (reader).</p>
</li>
<li><p>Запустим вторую goroutine, которая читает из <code>pending</code>, считает цифры и записывает в канал <code>counted</code> (worker).</p>
</li>
<li><p>Во внешней функции читаем из <code>counted</code> и обновляем итоговый счётчик <code>stats</code>.</p>
</li>
</ol>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">countDigitsInWords</span><span class="hljs-params">(next <span class="hljs-keyword">func</span>()</span> <span class="hljs-title">string</span>) <span class="hljs-title">counter</span></span> {
    pending := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">string</span>)
    counted := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> pair)

    <span class="hljs-comment">// отправляет слова для подсчёта</span>
    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-comment">// Получаем слова из генератора</span>
        <span class="hljs-comment">// и отправляем их в канал pending.</span>
    }()

    <span class="hljs-comment">// считает цифры в словах</span>
    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-comment">// Читаем слова из канала pending,</span>
        <span class="hljs-comment">// считаем количество цифр в каждом слове,</span>
        <span class="hljs-comment">// и отправляем результаты в канал counted.</span>
    }()

    <span class="hljs-comment">// Читаем значения из канала counted</span>
    <span class="hljs-comment">// и заполняем stats.</span>

    <span class="hljs-keyword">return</span> stats
}
</code></pre>
<hr />
<h2 id="heading-imenovannye-goroutines">Именованные goroutines</h2>
<p>После разделения логики между reader и worker функция стала довольно объёмной:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">countDigitsInWords</span><span class="hljs-params">(next <span class="hljs-keyword">func</span>()</span> <span class="hljs-title">string</span>) <span class="hljs-title">counter</span></span> {
    <span class="hljs-comment">// ...</span>

    <span class="hljs-comment">// отправляет слова для подсчёта</span>
    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-comment">// ...</span>
    }()

    <span class="hljs-comment">// считает цифры в словах</span>
    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-comment">// ...</span>
    }()

    <span class="hljs-comment">// заполняет stats</span>
    <span class="hljs-comment">// ...</span>

    <span class="hljs-keyword">return</span> stats
}
</code></pre>
<p>Чётко видны три логических блока:</p>
<ol>
<li><p>Отправка слов для подсчёта.</p>
</li>
<li><p>Подсчёт цифр в словах.</p>
</li>
<li><p>Заполнение итоговых результатов.</p>
</li>
</ol>
<p>Было бы удобно выделить эти блоки в отдельные функции, которые обмениваются данными через каналы:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">countDigitsInWords</span><span class="hljs-params">(next <span class="hljs-keyword">func</span>()</span> <span class="hljs-title">string</span>) <span class="hljs-title">counter</span></span> {
    pending := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">string</span>)
    <span class="hljs-keyword">go</span> submitWords(next, pending)

    counted := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> pair)
    <span class="hljs-keyword">go</span> countWords(pending, counted)

    <span class="hljs-keyword">return</span> fillStats(counted)
}
</code></pre>
<hr />
<h2 id="heading-vyhodnoj-kanal">Выходной канал</h2>
<p>Вот функция, к которой мы пришли:</p>
<pre><code class="lang-go"><span class="hljs-comment">// countDigitsInWords считает количество цифр в словах,</span>
<span class="hljs-comment">// получая следующее слово через next().</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">countDigitsInWords</span><span class="hljs-params">(next <span class="hljs-keyword">func</span>()</span> <span class="hljs-title">string</span>) <span class="hljs-title">counter</span></span> {
    pending := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">string</span>)
    <span class="hljs-keyword">go</span> submitWords(next, pending)

    counted := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> pair)
    <span class="hljs-keyword">go</span> countWords(pending, counted)

    <span class="hljs-keyword">return</span> fillStats(counted)
}
</code></pre>
<p>Она выглядит хорошо, но есть ещё одна вещь, которую мы могли бы изменить.</p>
<p>Канал <code>pending</code> создаётся в родительской функции только для передачи в дочернюю функцию <code>submitWords</code>. Лучше было бы создать канал в <code>submitWords</code> и вернуть его родителю, чтобы <code>submitWords</code> полностью владела им. То же самое относится к каналу <code>counted</code> и функции <code>countWords</code>.</p>
<p>Тогда <code>countDigitsInWords</code> будет выглядеть так:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">countDigitsInWords</span><span class="hljs-params">(next <span class="hljs-keyword">func</span>()</span> <span class="hljs-title">string</span>) <span class="hljs-title">counter</span></span> {
    pending := submitWords(next)
    counted := countWords(pending)
    <span class="hljs-keyword">return</span> fillStats(counted)
}
</code></pre>
<p>Теперь владение ясно, и программу легче понимать. Но куда делись все goroutines? Мы запускаем их внутри <code>submitWords</code> и <code>countWords</code> так:</p>
<pre><code class="lang-go"><span class="hljs-comment">// submitWords отправляет слова для подсчёта.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">submitWords</span><span class="hljs-params">(next <span class="hljs-keyword">func</span>()</span> <span class="hljs-title">string</span>) <span class="hljs-title">chan</span> <span class="hljs-title">string</span></span> {
    out := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">string</span>)
    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">for</span> {
            word := next()
            out &lt;- word
            <span class="hljs-keyword">if</span> word == <span class="hljs-string">""</span> {
                <span class="hljs-keyword">break</span>
            }
        }
    }()
    <span class="hljs-keyword">return</span> out
}

<span class="hljs-comment">// countWords считает цифры в словах.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">countWords</span><span class="hljs-params">(in <span class="hljs-keyword">chan</span> <span class="hljs-keyword">string</span>)</span> <span class="hljs-title">chan</span> <span class="hljs-title">pair</span></span> {
    out := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> pair)
    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">for</span> {
            word := &lt;-in
            count := countDigits(word)
            out &lt;- pair{word, count}
            <span class="hljs-keyword">if</span> word == <span class="hljs-string">""</span> {
                <span class="hljs-keyword">break</span>
            }
        }
    }()
    <span class="hljs-keyword">return</span> out
}
</code></pre>
<p>Убедимся, что программа всё ещё работает как ожидается:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    phrase := <span class="hljs-string">"0ne 1wo thr33 4068"</span>
    next := wordGenerator(phrase)
    stats := countDigitsInWords(next)
    printStats(stats)
}
</code></pre>
<p>У этого подхода есть недостаток: <code>submitWords</code> и <code>countWords</code> теперь более сложные и запускают goroutines. С другой стороны, <code>countDigitsInWords</code> проще и надёжнее (особенно если мы сделаем каналы направленными, что мы обсудим позже). Какой вариант выбрать, зависит от ваших предпочтений, но определённо не стоит смешивать два метода.</p>
<p>Возврат выходного канала из функции и заполнение его внутри внутренней goroutine — это распространённый паттерн в Go.</p>
<hr />
<h2 id="heading-rezyume">Резюме</h2>
<ul>
<li><p><strong>Goroutines</strong> — это легковесные потоки выполнения в Go, которые позволяют писать конкурентные программы.</p>
</li>
<li><p><strong>WaitGroup</strong> используется для синхронизации и ожидания завершения нескольких goroutines.</p>
</li>
<li><p><strong>Channels</strong> обеспечивают безопасную передачу данных между goroutines и их синхронизацию.</p>
</li>
<li><p>Разделение логики на <strong>reader</strong> и <strong>worker</strong> делает код более понятным и масштабируемым.</p>
</li>
<li><p>Паттерн <strong>выходного канала</strong> упрощает композицию конкурентных функций.</p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Postgres: Index Scans]]></title><description><![CDATA[Using an index to improve query performance is a fundamental database practice. An index is a specialized structure organized to make data access cheaper than scanning the entire disk. In PostgreSQL, the data stored on disk is referred to as the Heap...]]></description><link>https://blog.fshtab.com/postgres-index-scans</link><guid isPermaLink="true">https://blog.fshtab.com/postgres-index-scans</guid><dc:creator><![CDATA[Fedor S]]></dc:creator><pubDate>Sat, 20 Jan 2024 10:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1766397584479/43b49ddc-5b95-4b3e-b008-99987e68f0ef.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Using an index to improve query performance is a fundamental database practice. An index is a specialized structure organized to make data access cheaper than scanning the entire disk. In PostgreSQL, the data stored on disk is referred to as the <strong>Heap</strong>.</p>
<hr />
<h2 id="heading-the-reality-of-indexes">The Reality of Indexes</h2>
<p>Having an index on a table does <strong>not</strong> guarantee that it will be used. The PostgreSQL planner evaluates multiple execution paths and chooses the one with the lowest estimated cost. </p>
<p>It is vital to test and measure the effect of an index using the <code>EXPLAIN</code> command. Blindly adding indexes can actually degrade performance, as they increase the overhead for write operations (<code>INSERT</code>, <code>UPDATE</code>, <code>DELETE</code>) and consume disk space.</p>
<hr />
<h2 id="heading-1-practical-scenario-when-indexes-are-ignored">1. Practical Scenario: When Indexes are Ignored</h2>
<p>Let's create a table with 10 million records and a composite index to see how the planner behaves.</p>
<h3 id="heading-setup">Setup</h3>
<pre><code class="lang-sql"><span class="hljs-comment">-- Create table</span>
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> foo(id1 <span class="hljs-built_in">INT</span>, id2 <span class="hljs-built_in">INT</span>, id3 <span class="hljs-built_in">INT</span>, id4 <span class="hljs-built_in">INT</span>, <span class="hljs-keyword">descr</span> <span class="hljs-built_in">TEXT</span>);

<span class="hljs-comment">-- Insert 10M records</span>
<span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> foo 
<span class="hljs-keyword">SELECT</span> i, i*<span class="hljs-number">3</span>, i+i, i*<span class="hljs-number">2</span>, <span class="hljs-string">'hello'</span> || i 
<span class="hljs-keyword">FROM</span> generate_series(<span class="hljs-number">1</span>, <span class="hljs-number">10000000</span>) i;

<span class="hljs-comment">-- Create a composite index</span>
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">INDEX</span> idx_id1_id2_id3 <span class="hljs-keyword">ON</span> foo(id1, id2, id3);
</code></pre>
<h3 id="heading-running-explain">Running EXPLAIN</h3>
<p>If we run a query that filters for a large portion of the table:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">EXPLAIN</span> (<span class="hljs-keyword">ANALYZE</span>, BUFFERS) <span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> foo <span class="hljs-keyword">WHERE</span> id1 &gt; <span class="hljs-number">1000</span>;
</code></pre>
<p><strong>Planner Output:</strong></p>
<pre><code>Seq Scan on foo  (cost=<span class="hljs-number">0.00</span>.<span class="hljs-number">.198530</span><span class="hljs-number">.00</span> rows=<span class="hljs-number">9999027</span> width=<span class="hljs-number">28</span>) (actual time=<span class="hljs-number">0.283</span>.<span class="hljs-number">.2260</span><span class="hljs-number">.973</span> rows=<span class="hljs-number">9999000</span> loops=<span class="hljs-number">1</span>)
   <span class="hljs-attr">Filter</span>: (id1 &gt; <span class="hljs-number">1000</span>)
   Rows Removed by Filter: <span class="hljs-number">1000</span>
   <span class="hljs-attr">Buffers</span>: shared hit=<span class="hljs-number">16274</span> read=<span class="hljs-number">57256</span>
</code></pre><p><strong>Why was the Sequential Scan chosen?</strong> When using an index, the database must read the index structure and then fetch the relevant data pages from the heap. This often results in 2 disk I/Os per row. If the query returns almost the entire table (low selectivity), reading the file linearly (Seq Scan) is much faster than jumping between the index and the heap.</p>
<hr />
<h2 id="heading-2-analyzing-alternative-plans">2. Analyzing Alternative Plans</h2>
<p>Standard EXPLAIN only shows the "winning" plan. To understand why the index was rejected, we can use an extension like PG_ALL_PLANS to see all considered paths.</p>
<p><strong>Comparative Costs for id1 &gt; 1000:</strong></p>
<ul>
<li>Plan 1 (Seq Scan): Cost 198,530.00 (Winner).</li>
<li>Plan 2 (Index Scan): Cost 498,815.28 (Rejected due to high I/O cost).</li>
<li>Plan 3 (Bitmap Heap Scan): Cost 429,813.47 (Rejected; lower than index scan but higher startup than seq scan).</li>
<li>Plan 4 (Parallel Seq Scan): Cost 1,126,516.03 (Rejected).</li>
</ul>
<hr />
<h2 id="heading-3-increasing-selectivity-to-force-index-usage">3. Increasing Selectivity to Force Index Usage</h2>
<p>We can persuade the planner to use the index by reducing the number of rows returned. This increases the selectivity of the query.</p>
<pre><code class="lang-sql"><span class="hljs-keyword">EXPLAIN</span> (<span class="hljs-keyword">ANALYZE</span>, BUFFERS) 
<span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> foo <span class="hljs-keyword">WHERE</span> id1 &gt; <span class="hljs-number">1000</span> <span class="hljs-keyword">AND</span> id1 &lt; <span class="hljs-number">1500000</span>;
</code></pre>
<p><strong>Updated Result:</strong></p>
<pre><code>Index Scan using idx_id1_id2_id3 on foo  (cost=<span class="hljs-number">0.43</span>.<span class="hljs-number">.187892</span><span class="hljs-number">.55</span> rows=<span class="hljs-number">1498365</span> width=<span class="hljs-number">28</span>)
   Index Cond: ((id1 &gt; <span class="hljs-number">1000</span>) AND (id1 &lt; <span class="hljs-number">1500000</span>))
   <span class="hljs-attr">Buffers</span>: shared hit=<span class="hljs-number">2</span> read=<span class="hljs-number">16768</span>
</code></pre><p>Now, the Index Scan cost (~187k) is lower than the Seq Scan cost (~223k). Because we are only fetching ~1.5M rows instead of 10M, the index becomes the most efficient path.</p>
<hr />
<h2 id="heading-summary-amp-best-practices">Summary &amp; Best Practices</h2>
<ul>
<li><p><strong>Selectivity is Key:</strong> Indexes are most effective when they filter out the vast majority of the data.</p>
</li>
<li><p><strong>Use EXPLAIN ANALYZE:</strong> Always verify your performance assumptions in a staging environment.</p>
</li>
<li><p><strong>Monitor Usage:</strong> Indexes are not free. Use <code>pg_stat_user_indexes</code> to identify and remove unused indexes that slow down writes.</p>
</li>
<li><p><strong>Analyze Statistics:</strong> Ensure the planner has up-to-date data by running the <code>ANALYZE</code> command regularly.</p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[gRPC versus REST: A Performance Comparison]]></title><description><![CDATA[gRPC versus REST: A Performance Comparison
TL;DR:We benchmarked REST vs gRPC under identical local setups using .NET. While gRPC is widely believed to outperform REST, our results show REST can be equally, or even slightly faster in specific data-hea...]]></description><link>https://blog.fshtab.com/grpc-vs-rest-performance-comparison</link><guid isPermaLink="true">https://blog.fshtab.com/grpc-vs-rest-performance-comparison</guid><dc:creator><![CDATA[Fedor S]]></dc:creator><pubDate>Mon, 15 Jan 2024 11:30:00 GMT</pubDate><enclosure url="https://blog.postman.com/wp-content/uploads/2023/11/gRPC-vs-REST-1.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-grpc-versus-rest-a-performance-comparison">gRPC versus REST: A Performance Comparison</h1>
<p><strong>TL;DR:</strong><br />We benchmarked REST vs gRPC under identical local setups using .NET. While gRPC is widely believed to outperform REST, our results show REST can be equally, or even slightly faster in specific data-heavy operations.</p>
<p>As microservices multiply across modern systems, the choice of API protocol can make or break performance. REST has ruled for years, but gRPC promises to be faster, leaner and more efficient. So why hasn't it taken over? Microservices can be heavily dependent on each other, which means speed and stability is key. When gRPC claims to be faster than REST, why isn't it the de facto standard? In this blog we will put gRPC and REST head to head, to see which is actually faster.</p>
<p><strong>gRPC is a superior technology to REST!</strong> At least that is what multiple sources claim. According to various blogs, gRPC performs better and faster than REST on several metrics. In this blog we will test specifically, how <strong><em>fast</em></strong> a REST client can handle different requests and responses, and compare it to how fast a similar gRPC client handles the same requests and responses. This begs the question...</p>
<p><code>Is gRPC faster than REST?</code></p>
<p><em>We hypothesize that gRPC is able to send and receive requests faster than a traditional REST.</em> To test this, the following experiments have been developed.</p>
<hr />
<h2 id="heading-the-experiment">The Experiment</h2>
<p>To test the hypothesis, two experiments were created, one utilizing gRPC and one utilizing REST. These experiments have to adhere to the following:</p>
<p><strong>Rules</strong></p>
<ul>
<li>To ensure accurate measurements, the results must be obtained from the same computer.</li>
<li>Multiple data structures will be tested.</li>
<li>The setup for both APIs has to be as similar as possible.</li>
<li>The time used for measuring should be obtained from the client.</li>
</ul>
<p><strong>Set up</strong></p>
<ul>
<li>To adhere to the multiple data structures rule, a local database has been created, this database will provide a single instance of an object, as well as multiple instances of objects that will be stored in a list.</li>
<li>Each API will have 12 methods to call.  <ul>
<li>Three for a single instance which takes a parameter of Id.  <ul>
<li>Each one with a larger payload  </li>
</ul>
</li>
<li>Three for a single instance which takes a parameter of Id.  <ul>
<li>Each one with a deeper payload  </li>
</ul>
</li>
<li>Three for a collection of 100 instances, which takes no parameters.  <ul>
<li>Each one with a larger payload  </li>
</ul>
</li>
<li>Three for a collection of 100 instances, which takes no parameters.  <ul>
<li>Each one with a deeper payload</li>
</ul>
</li>
</ul>
</li>
<li>Each API will be tested with a client written in C#, as a console app.  <ul>
<li>Time will be measured with .NET Stopwatch.  </li>
<li>The stopwatch will begin when the method is called and end when the API returns the full data.</li>
</ul>
</li>
<li>To minimize anomalies and outliers, each operation will be executed 100 times, and the average call speed will be evaluated.</li>
</ul>
<p><strong>Specs</strong></p>
<p>I7-9700k, 8 core, <em>4.6GHz</em></p>
<p>Samsung SSD 840 EVO 250GB</p>
<p>NVIDIA GeForce GTX 1070</p>
<p>2x8 GB HyperX Fury 2666MHz DDR4 Memory</p>
<hr />
<h2 id="heading-rest">REST</h2>
<h3 id="heading-what-is-rest">What is REST?</h3>
<p>We have decided to work with the common implementation of REST and not the full implementation of a RESTful API.</p>
<p>The key features to take note of when using REST:</p>
<p><strong>Separation of client and server</strong></p>
<ul>
<li>Server and client can be implemented independently without knowing each other.</li>
<li>Server code can be changed without affecting the client.</li>
<li>Client code can be changed without affecting the server.</li>
<li>Both server and client are aware of methods available.</li>
</ul>
<p><strong>Statelessness</strong></p>
<ul>
<li>Stateless means that the server is not required to know the current state of the client and vice versa.</li>
<li>Either end can understand any method calls, without knowing the previously called methods.</li>
</ul>
<p><strong>Invocation</strong></p>
<ul>
<li>We invoke a method on the server via HTTP operations  <ul>
<li>GET  </li>
<li>POST  </li>
<li>PUT  </li>
<li>DELETE</li>
</ul>
</li>
</ul>
<hr />
<h2 id="heading-setting-up-the-experiment-for-the-rest-api">Setting Up the Experiment for the REST API</h2>
<p>The architecture for this experiment is a simple one:</p>
<p>REST Architecture</p>
<hr />
<h2 id="heading-sample-project-and-metrics">Sample Project and Metrics</h2>
<p>If you want to replicate this experiment yourself, the database setup can be found in the repository and the source code for the rest-api can be found in the repository.</p>
<p>Running our setup yielded the following results:</p>
<p><strong>Single payload</strong></p>
<p>The difference between a single small payload and a single large payload is small in the context of a daily task. A single small payload has a mean response time of 0.0181 whilst a single large payload has a mean response time of 0.0204 seconds. But in relation to each other, it's a 12.7% increase in response time.</p>
<p>API Single Payload</p>
<p>To put this into perspective a small payload contains 10 values of data. A large payload contains (4+(6<em>9))</em>6+4 or 352 values. This means that we have requested 3420% more data and it only took 12.7% longer.</p>
<p>To test different scenarios we also created a "deep" payload that contains a different amount of nested objects. The deepest payload contains a total of eight nested objects, however, the total amount of values is far less in comparison to the previous payload. The previously mentioned payload peaked at 352 values whereas the deepest payload peaks at (4+(6<em>4))+(4+(7</em>4))+(4+(8*4)) values, or 96 values in total. In other words, the deep payloads are much smaller in size but different in structure.</p>
<p>To give a concrete example, a large payload is structured like so:  </p>
<pre><code>large_payload {
    id,
    string_Value,
    int_value,
    double_value,
    medium_...(<span class="hljs-number">2743</span> chars omitted)...s.
</code></pre><hr />
<h2 id="heading-grpc">gRPC</h2>
<h3 id="heading-what-is-grpc">What is gRPC?</h3>
<p>gRPC is a modern, open-source remote procedure call (RPC) framework that can run anywhere. It enables client and server applications to communicate transparently and develop connected systems.</p>
<p>Some key features we would like to highlight:</p>
<p><strong>HTTP/2 support</strong></p>
<p>HTTP/2 is HTTP/1's successor, which is what most websites and frameworks utilize today. In many ways, HTTP/2 is an improved version of HTTP/1, and HTTP/3 is already in the works.</p>
<p><strong>Language independent</strong></p>
<p>gRPC is language independent, which means it doesn't matter which language you develop in. The framework supports a handful of popular languages. This is quite an advantage when you're developing microservices, which might have services developed in different languages and frameworks.</p>
<p><strong>Contract First</strong></p>
<p>gRPC is strictly contract first which is a design approach that works especially well in larger development teams. It also excels when developing microservices, as a contract would be created before any actual implementations can be done. The contract is designed in the .proto file, which is also where gRPC gains some of its speed from, seeing as .proto files are...</p>
<p><strong>Strongly typed</strong> As a by-product of a strongly typed proto file, which is used as a contract between client and server, but also used as an extensible mechanism for serializing structured data.</p>
<hr />
<h2 id="heading-setting-up-the-grpc-project">Setting Up the gRPC Project</h2>
<p>For the gRPC architecture we use the same as the REST, we have a client and a server running locally. The client calls the methods exposed by the proto file. The method then gets executed on the server and queries the database, once the data has been obtained it replies to the client. When the client has received all the data, we stop and log the time elapsed since the call started.</p>
<p>gRPC Architecture</p>
<hr />
<h3 id="heading-sample-project-and-metrics-1">Sample Project and Metrics</h3>
<p>If you want to replicate this experiment yourself, database setup can be found in the repository and the source code for the grpc-project can be found in the repository.</p>
<p>Running our setup yielded the following results:</p>
<p><strong>Single payloads</strong></p>
<p>gRPC Wide Payload Results</p>
<p>The test on single payloads yielded quite odd results, where the medium payload proved to be the fastest on average, and the largest payload only being slightly slower than the smallest. Just to recap the numbers; a larger payload contains 3400% more data than a small payload, and yet it only took 2.36% longer to get that data.</p>
<p>gRPC Deep Payload Results</p>
<p>Even more odd were the results of the deep payloads. Once again the payload containing a "medium" amount of data, was the fastest, just like previously. But unlike previously, the deepest payload was significantly faster than the deep payload, to be precise; the deepest payload, which contains 300% more data than a deep payload was 28.63% faster. As the results are rather unexpected we have to take a close look at possible errors that could have occurred.</p>
<p><strong>Collection of payloads</strong></p>
<p>gRPC Wide Payload Results</p>
<p>Collections paint a different picture, a small payload collection averaged 0.01911 seconds, while a collection of large payloads took 0.7025. This means a large payload on average took 267.6% longer to get. These results are much closer to what we would expect. We did the same test with a collection of deep payloads.</p>
<p>gRPC Deep Payload Collection Results</p>
<p>The results of this test, were as you would expect, as the payloads incrementally increase in size, they also increase incrementally in response time.</p>
<hr />
<h2 id="heading-conclusion">Conclusion</h2>
<p>When we put the two charts next to each other, it's easy to see which one has an edge. The REST API is represented by the blue blocks, whilst gRPC is represented by the red blocks, just like previously.</p>
<p>Wide Payload results comparison</p>
<p>Deep payload results comparison</p>
<p>This is the case for both single instances of objects as well as collections of objects.</p>
<p>Deep payload collection result comparison</p>
<p>Wide payload collection result comparison</p>
<p>We hypothesized that gRPC would be faster than REST, based on the numerous blogs claiming this to be true, with their own tests. Contrary to popular belief, our experiments suggest that under certain conditions, REST can outperform gRPC in raw speed, reminding us that performance isn't universal but contextual.</p>
<p>These results might not seem as much, but it has been proven that people on average don't wait around for data to load and will abandon a web page or program if loading times are too long. When moving large amounts of data, a small amount of time can be the difference between keeping or losing a customer.</p>
<p>This prompts the question: <strong><em>When to use gRPC and when to use REST</em></strong></p>
<p>We would argue that gRPC fits into a setting, where you need to have multiple programs or services talking to each other across different languages, especially when the task that needs to connect to an endpoint is an action that needs to be executed; one such action could be TurnOnTheWater(). This argument is based on the research made into gRPC, rather than the results of these particular tests.</p>
<p>REST on the other hand operates on the four aforementioned HTTP operations, these operations indicated data transfers of one sort or the other. While REST can execute the same actions as gRPC, the action TurnOnTheWater() doesn't fit into what a REST API was designed for. We would instead use REST where we required data transfers and other typical CRUD mechanics.</p>
<p>Ultimately, the right choice depends on your use case: REST for simplicity and interoperability, gRPC for high-efficiency internal microservice communication.</p>
<hr />
<h2 id="heading-possible-errors">Possible Errors</h2>
<ul>
<li>Network outage during some of the tests</li>
<li>The gRPC serverside logging was set to critical, tweaking this option might yield different results.</li>
</ul>
<hr />
<h2 id="heading-whats-next">What's Next?</h2>
<p>This blog has only been about the differences in speed between REST and gRPC, but in reality, many other factors are present, if we were to truly compare the two frameworks. gRPC has claimed to not only be faster, but also more reliable, stable, and secure, and all of these metrics, as well as other metrics, would be interesting to cover, they are however out of scope in this particular blog-post.</p>
<hr />
<h2 id="heading-technologies-used">Technologies Used</h2>
<ul>
<li>gRPC</li>
<li>.NET Web Api</li>
<li>.NET console app</li>
<li>MySQL Database</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[ClickHouse: Advantages, Challenges, and Pitfalls]]></title><description><![CDATA[ClickHouse: Advantages, Challenges, and Pitfalls
ClickHouse is one of those databases that generates excitement after the first benchmark. It's extremely fast, column-oriented, and designed for analytics at scale.
It's also remarkably simple to integ...]]></description><link>https://blog.fshtab.com/clickhouse-the-good-the-bad-and-the-ugly</link><guid isPermaLink="true">https://blog.fshtab.com/clickhouse-the-good-the-bad-and-the-ugly</guid><dc:creator><![CDATA[Fedor S]]></dc:creator><pubDate>Thu, 28 Dec 2023 14:15:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1766406486476/300b2373-b249-416d-a2f3-0aaa614ba72f.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-clickhouse-advantages-challenges-and-pitfalls">ClickHouse: Advantages, Challenges, and Pitfalls</h1>
<p>ClickHouse is one of those databases that generates excitement after the first benchmark. It's extremely fast, column-oriented, and designed for analytics at scale.</p>
<p>It's also remarkably simple to integrate into an existing infrastructure. You can stream data from Postgres, MongoDB, S3, or virtually any source. That's what makes it so attractive. You reach the point where your Postgres queries begin to struggle, you don't want to rebuild everything, so you introduce ClickHouse and suddenly your dashboard loads in milliseconds.</p>
<p>It's like adding a turbocharger to your reporting system.</p>
<p>ClickHouse is also evolving at an incredible pace. Every month they release new features, bug fixes, and faster query performance.</p>
<p>But with speed comes responsibility. ClickHouse is a powerful system. It'll reward you when you treat it properly, but it'll cause problems if you take shortcuts.</p>
<hr />
<h2 id="heading-cloud-vs-self-hosting">Cloud vs Self-Hosting</h2>
<p>Your first major decision is whether to self-host or choose a managed provider.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Cloud</td><td>Self-Hosted</td></tr>
</thead>
<tbody>
<tr>
<td><strong>Setup time</strong></td><td>Minutes</td><td>Days/weeks</td></tr>
<tr>
<td><strong>Cost at scale</strong></td><td>$$$</td><td>$ + engineer time</td></tr>
<tr>
<td><strong>Backup/HA</strong></td><td>Automatic</td><td>DIY</td></tr>
<tr>
<td><strong>Headaches</strong></td><td>A few but not hosting related</td><td>Many</td></tr>
<tr>
<td><strong>Good for</strong></td><td>Most use cases</td><td>Cost optimization</td></tr>
</tbody>
</table>
</div><p>The cloud approach is straightforward. You get uptime, automatic scaling, and minimal headaches. The trade-off is cost. ClickHouse Cloud, Altinity Cloud, Tinybird, they all work well, but the bill can become significant once you start moving large amounts of data. You don't deal with server issues, but you pay for that peace of mind.</p>
<p>In ClickHouse Cloud you also don't need to worry about replication, as this is handled automatically so you don't need to create replicated and distributed tables.</p>
<p>Self-hosting appears simple at first, but it's not.</p>
<p>You set up a single node, everything runs smoothly, and then one day something fails, stops merging data, corrupts data, or something else goes wrong. This is just the tip of the iceberg.</p>
<p>To handle real production traffic you'll end up with replicated and distributed tables. You'll need to choose between vertical scaling, horizontal scaling, or both. Then you start worrying about corrupted parts, cluster topology, and backups.</p>
<p>Running ClickHouse yourself works fine for smaller setups. Once you grow, it's a full-time job unless you use something like the <strong>Altinity ClickHouse Operator</strong> on Kubernetes. That operator makes things manageable. You define clusters in YAML, it handles replication, ZooKeeper (ClickHouse Keeper), and has excellent backup strategies. If you ever plan to self-host long-term, start there.</p>
<hr />
<h2 id="heading-the-dark-side-of-joins">The Dark Side of Joins</h2>
<p>Joins in ClickHouse are not the joins you're accustomed to. They work, but they're not "free."</p>
<p>ClickHouse doesn't have a full query optimizer like Postgres or MySQL. That means it doesn't plan your joins intelligently. If you join two large tables, it'll happily try to load everything in memory and fail in the process.</p>
<p>You have to think ahead. Filter first, join later.</p>
<p>A few ways to survive:</p>
<ul>
<li>Use <strong>CTEs</strong> or sub-queries to narrow down the joined dataset before the join actually happens.</li>
<li>Use <strong>dictionaries</strong> (in-memory lookup tables) for small reference data. They're extremely fast, but they have to fit in memory.</li>
<li>Know your <strong>sorting keys</strong>. ClickHouse relies on them for efficient reads. Poor keys make joins worse.</li>
<li>Always join the smaller table</li>
</ul>
<p>You'll notice when you have a bad join since it will take a long time or fail completely.</p>
<hr />
<h2 id="heading-updates-deletes-and-the-reality-of-immutability">Updates, Deletes, and the Reality of Immutability</h2>
<p>ClickHouse was never designed for frequent updates or deletes. It's a write-once, append-forever kind of database. You can't just run <code>UPDATE users SET ...</code> like in Postgres.</p>
<p>To their credit, the ClickHouse team has made significant progress here. They've added <strong>lightweight deletes and updates</strong>, and there are new table engines like <code>ReplacingMergeTree</code> and <code>VersionedCollapsingMergeTree</code> that can simulate mutable data. But it still requires extra consideration.</p>
<p>You need to design your tables knowing that changing data later is more difficult. That's fine for analytics workloads, but painful if you expect relational behavior.</p>
<p>These kinds of issues still occur today. Hoping the lightweight updates which is in beta now will make life easier.</p>
<hr />
<h2 id="heading-inserting-data-the-right-way">Inserting Data the Right Way</h2>
<p>Here's the biggest beginner trap.</p>
<p>ClickHouse loves large inserts. It hates small ones.</p>
<p>Every insert triggers background merges, index updates, compression, and part creation. Do that one row at a time and you'll overwhelm it. Batch your inserts into chunks, ideally thousands of rows at a time. You'll immediately see CPU drop and throughput increase dramatically.</p>
<p>If you're ingesting data continuously, throw it into a queue and batch it there. That's what we do at OpenPanel.dev. It smooths out traffic spikes and keeps our ingestion fast and predictable.</p>
<hr />
<h2 id="heading-replication-and-sharding">Replication and Sharding</h2>
<p>This isn't a bad thing about ClickHouse. In fact, it's one of its best features.<br />But I still want to cover a few parts that confused me when I first started using it.</p>
<p>There are three kinds of tables you'll deal with when setting up replication or sharding:</p>
<ul>
<li>Your existing table (usually <code>MergeTree</code> or something similar)</li>
<li>A replicated table (<code>ReplicatedMergeTree</code>)</li>
<li>A distributed table (<code>Distributed</code>)</li>
</ul>
<p>Each of these plays a different role in your cluster, and it's worth understanding them before you begin.</p>
<hr />
<p>First, decide how many replicas you want. Most setups use three replicas for high availability and to get replication working, you'll replace your existing table with a replicated one.</p>
<p>You can do that by creating a new table and swapping <code>MergeTree</code> for <code>ReplicatedMergeTree</code> in the engine section.  </p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> events_replicated <span class="hljs-keyword">ON</span> CLUSTER <span class="hljs-string">'{cluster}'</span> (
  ...
)
<span class="hljs-keyword">ENGINE</span> = ReplicatedMergeTree(
  <span class="hljs-string">'/clickhouse/{installation}/{cluster}/tables/{shard}/openpanel/v1/{table}'</span>,
  <span class="hljs-string">'{replica}'</span>
)
<span class="hljs-keyword">PARTITION</span> <span class="hljs-keyword">BY</span> toYYYYMM(created_at)
<span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> (created_at)
</code></pre>
<p>Once that's done, any data written to one node will be replicated to the others. ZooKeeper or ClickHouse Keeper handles the synchronization automatically.</p>
<p>If you want to move your data from your existing table to the replicated table you can use <code>INSERT SELECT</code> to do this.  </p>
<pre><code class="lang-sql"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> events_replicated <span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> <span class="hljs-keyword">events</span>;
</code></pre>
<hr />
<p>Now let's look at where sharding and <code>Distributed</code> tables come in.<br />Sharding is how you scale horizontally by splitting data into smaller chunks and spreading them across nodes. That said, it's usually better to scale vertically first, because ClickHouse handles vertical scaling surprisingly well.</p>
<p>If you decide to shard, you'll need to create a distributed table. A distributed table knows where your data lives and redirects queries to the right node.</p>
<p>When creating one, you define how data should be split across nodes. In the example below, the data is sharded using <code>cityHash64(project_id)</code>, which spreads rows evenly based on the <code>project_id</code>.  </p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> events_distributed <span class="hljs-keyword">ON</span> CLUSTER <span class="hljs-string">'{cluster}'</span> <span class="hljs-keyword">AS</span> events_replicated
<span class="hljs-keyword">ENGINE</span> = <span class="hljs-keyword">Distributed</span>(
  <span class="hljs-string">'{cluster}'</span>,
  currentDatabase(),
  events_replicated,
  cityHash64(project_id)
)
</code></pre>
<p>Now you can query data from any node, and ClickHouse will automatically route the request to where the data actually sits.</p>
<p>If you want to dig deeper, check out the official docs on Distributed tables and Replication.</p>
<hr />
<h2 id="heading-so-why-stick-with-it">So Why Stick With It?</h2>
<p>Because when it works, it's magic.</p>
<p>At OpenPanel we hit all these issues. Slow inserts, bad joins, tricky replication, and we still use ClickHouse every single day. Once you set it up correctly, nothing else compares. It's incredibly fast and scales far beyond what most relational databases can handle.</p>
<p>You just have to respect it. Treat it like a Ferrari, not a Corolla.</p>
<p>If you want me to go deeper into how we deploy and manage our own cluster on Kubernetes using the Altinity operator, let me know in the comments. I can show exactly how we keep it stable and cost-efficient.</p>
]]></content:encoded></item><item><title><![CDATA[Go: Wait Groups and Coordinating Multiple Goroutines]]></title><description><![CDATA[Channels are a multi-purpose concurrency tool in Go. In Part 1 of the book, we covered their main use cases:

Transferring data between goroutines.
Synchronizing goroutines (the done channel).
Canceling goroutines (the cancel channel).

Transferring ...]]></description><link>https://blog.fshtab.com/go-wait-groups-goroutine-synchronization</link><guid isPermaLink="true">https://blog.fshtab.com/go-wait-groups-goroutine-synchronization</guid><dc:creator><![CDATA[Fedor S]]></dc:creator><pubDate>Mon, 11 Dec 2023 15:10:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1766406515384/947de4d3-bec0-4ede-bd0f-9b74327574a5.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Channels are a multi-purpose concurrency tool in Go. In Part 1 of the book, we covered their main use cases:</p>
<ul>
<li>Transferring data between goroutines.</li>
<li>Synchronizing goroutines (the <em>done</em> channel).</li>
<li>Canceling goroutines (the <em>cancel</em> channel).</li>
</ul>
<p>Transferring data is what channels were designed for, and they excel at it. For canceling goroutines, there is a special tool besides channels — a <em>context</em> (which we've also discussed). For synchronizing goroutines, there is also a special tool — a <em>wait group</em>. Let's talk about it.</p>
<hr />
<h2 id="heading-wait-group">Wait Group</h2>
<p>A wait group lets you wait for one or more goroutines to finish. We started with a wait group in the very first chapter on goroutines, and now we'll go into more detail.</p>
<p>Suppose we want to start a goroutine and wait for it to complete. Here's how to do it with a done channel:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    done := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">struct</span>{}, <span class="hljs-number">1</span>)

    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        time.Sleep(<span class="hljs-number">50</span> * time.Millisecond)
        fmt.Print(<span class="hljs-string">"."</span>)
        done &lt;- <span class="hljs-keyword">struct</span>{}{}
    }()

    &lt;-done
    fmt.Println(<span class="hljs-string">"done"</span>)
}
</code></pre>
<pre><code>.done
</code></pre><p>And here's how to do it with a wait group:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">var</span> wg sync.WaitGroup

    wg.Add(<span class="hljs-number">1</span>)
    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        time.Sleep(<span class="hljs-number">50</span> * time.Millisecond)
        fmt.Print(<span class="hljs-string">"."</span>)
        wg.Done()
    }()

    wg.Wait()
    fmt.Println(<span class="hljs-string">"done"</span>)
}
</code></pre>
<pre><code>.done
</code></pre><p>Interestingly, a <code>WaitGroup</code> doesn't know anything about the goroutines it manages. It works with an internal counter. Calling <code>wg.Add(1)</code> increments the counter by one, while <code>wg.Done()</code> decrements it. <code>wg.Wait()</code> blocks the calling goroutine (in this case, <code>main</code>) until the counter reaches zero. So, <code>main()</code> waits for the called goroutine to finish before exiting.</p>
<p>The <code>WaitGroup.Go</code> method (Go 1.25+) automatically increments the wait group counter, runs a function in a goroutine, and decrements the counter when it's done. This means we can rewrite the example above without using <code>wg.Add()</code> and <code>wg.Done()</code>:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">var</span> wg sync.WaitGroup

    wg.Go(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        time.Sleep(<span class="hljs-number">50</span> * time.Millisecond)
        fmt.Print(<span class="hljs-string">"."</span>)
    })

    wg.Wait()
    fmt.Println(<span class="hljs-string">"done"</span>)
}
</code></pre>
<pre><code>.done
</code></pre><p>In short, you can wait for a goroutine to finish using these methods:</p>
<ul>
<li>Done channel.</li>
<li>Wait group <code>Add</code>+<code>Done</code>+<code>Wait</code>.</li>
<li>Wait group <code>Go</code>+<code>Wait</code>.</li>
</ul>
<p>Typically, if you just need to wait for goroutines to complete without needing a result from them, you use a wait group instead of a done channel. The default choice should be the <code>Go</code> method, but in this chapter, I'll use <code>Add</code>+<code>Done</code> a lot because they do a better job of showing how things work internally.</p>
<hr />
<h2 id="heading-inner-world">Inner World</h2>
<p>As we discussed, the wait group knows nothing about goroutines and works with a counter instead. This simplifies the implementation a lot. Conceptually, you can think of the wait group like this:</p>
<pre><code class="lang-go"><span class="hljs-comment">// A WaitGroup waits for a collection of goroutines to finish.</span>
<span class="hljs-keyword">type</span> WaitGroup <span class="hljs-keyword">struct</span> {
    n <span class="hljs-keyword">int</span>
}

<span class="hljs-comment">// Add adds delta to the WaitGroup counter.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(wg *WaitGroup)</span> <span class="hljs-title">Add</span><span class="hljs-params">(delta <span class="hljs-keyword">int</span>)</span></span> {
    wg.n += delta
    <span class="hljs-keyword">if</span> wg.n &lt; <span class="hljs-number">0</span> {
        <span class="hljs-built_in">panic</span>(<span class="hljs-string">"negative counter"</span>)
    }
}

<span class="hljs-comment">// Done decrements the WaitGroup counter by one.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(wg *WaitGroup)</span> <span class="hljs-title">Done</span><span class="hljs-params">()</span></span> {
    wg.Add(<span class="hljs-number">-1</span>)
}

<span class="hljs-comment">// Wait blocks until the WaitGroup counter is zero.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(wg *WaitGroup)</span> <span class="hljs-title">Wait</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">for</span> wg.n &gt; <span class="hljs-number">0</span> {}
}
</code></pre>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">var</span> wg WaitGroup

    wg.Add(<span class="hljs-number">1</span>)
    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        time.Sleep(<span class="hljs-number">50</span> * time.Millisecond)
        fmt.Print(<span class="hljs-string">"."</span>)
        wg.Done()
    }()

    wg.Wait()
    fmt.Println(<span class="hljs-string">"done"</span>)
}
</code></pre>
<pre><code>.done
</code></pre><p>Of course, in practice it's more complicated:</p>
<ul>
<li>All methods can be called concurrently from multiple goroutines. Modifying the shared variable <code>n</code> from multiple goroutines is unsafe — concurrent access can corrupt data (we'll talk more about this in the chapter on data races).</li>
<li>A loop-based <code>Wait</code> implementation will max out a CPU core until the loop finishes (this type of waiting is also known as <em>busy waiting</em>). Such code is strongly discouraged in production.</li>
</ul>
<p>However, our naive implementation shows the properties of a wait group that are also present in the actual <code>sync.WaitGroup</code>:</p>
<ul>
<li><code>Add</code> increments or decrements (if <code>delta &lt; 0</code>) the counter. Positive deltas are much more common, but technically nothing prevents you from calling <code>Add(-1)</code>.</li>
<li><code>Wait</code> blocks execution until the counter reaches 0. So if you call <code>Wait</code> before the first <code>Add</code>, the goroutine won't block.</li>
<li>After <code>Wait</code> completes, the wait group returns to its initial state (counter is 0). You can then reuse it.</li>
</ul>
<p>As for the <code>Go</code> method, it's a simple wrapper that combines <code>Add</code> and <code>Done</code>. Here's the complete implementation taken directly from the standard library code:</p>
<pre><code class="lang-go"><span class="hljs-comment">// https://github.com/golang/go/blob/master/src/sync/waitgroup.go</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(wg *WaitGroup)</span> <span class="hljs-title">Go</span><span class="hljs-params">(f <span class="hljs-keyword">func</span>()</span>)</span> {
    wg.Add(<span class="hljs-number">1</span>)
    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">defer</span> wg.Done()
        f()
    }()
}
</code></pre>
<p>Try changing the example above from <code>Add</code>+<code>Done</code> to <code>Go</code> and see if it works.</p>
<hr />
<h2 id="heading-value-vs-pointer">Value vs. Pointer</h2>
<p>Another important implementation nuance: you should pass the wait group as a pointer (<code>*WaitGroup</code>), not as a value (<code>WaitGroup</code>). Otherwise, each recipient will get its own copy with a duplicate counter, and synchronization won't work.</p>
<p>Here's an example of passing a value:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">runWork</span><span class="hljs-params">(wg sync.WaitGroup)</span></span> {
    wg.Add(<span class="hljs-number">1</span>)
    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        time.Sleep(<span class="hljs-number">50</span> * time.Millisecond)
        fmt.Println(<span class="hljs-string">"work done"</span>)
        wg.Done()
    }()
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">var</span> wg sync.WaitGroup
    runWork(wg)
    wg.Wait()
    fmt.Println(<span class="hljs-string">"all done"</span>)
}
</code></pre>
<pre><code>all done
</code></pre><p><code>runWork</code> got a copy of the group and increased its counter with <code>Add</code>. Meanwhile, <code>main</code> has its own copy with a zero counter, so <code>Wait</code> didn't block execution. As a result, <code>main</code> finished without waiting for the <code>runWork</code> goroutine to complete.</p>
<p>Here's an example of passing a pointer:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">runWork</span><span class="hljs-params">(wg *sync.WaitGroup)</span></span> {
    wg.Add(<span class="hljs-number">1</span>)
    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        time.Sleep(<span class="hljs-number">50</span> * time.Millisecond)
        fmt.Println(<span class="hljs-string">"work done"</span>)
        wg.Done()
    }()
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">var</span> wg sync.WaitGroup
    runWork(&amp;wg)
    wg.Wait()
    fmt.Println(<span class="hljs-string">"all done"</span>)
}
</code></pre>
<pre><code>work done
all done
</code></pre><p>Now <code>runWork</code> and <code>main</code> share the same instance of the group, so everything works as it should.</p>
<p>An even better approach would be not to pass the wait group around at all. Instead, we can encapsulate it in a separate type that hides the implementation details and provides a nice interface. Let's see how to do that.</p>
<hr />
<h2 id="heading-encapsulation">Encapsulation</h2>
<p>In Go, it's considered a good practice to hide synchronization details from clients calling your code. Fellow developers won't thank you for forcing them to deal with wait groups. It's better to encapsulate the synchronization logic in a separate function or type, and provide a convenient interface.</p>
<h3 id="heading-wrapper-functions">Wrapper Functions</h3>
<p>Let's say I wrote a function called <code>RunConc</code> that runs a set of given functions concurrently:</p>
<pre><code class="lang-go"><span class="hljs-comment">// RunConc executes functions concurrently.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">RunConc</span><span class="hljs-params">(wg *sync.WaitGroup, funcs ...<span class="hljs-keyword">func</span>()</span>)</span> {
    wg.Add(<span class="hljs-built_in">len</span>(funcs))
    <span class="hljs-keyword">for</span> _, fn := <span class="hljs-keyword">range</span> funcs {
        <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
            <span class="hljs-keyword">defer</span> wg.Done()
            fn()
        }()
    }
}
</code></pre>
<p>A client would use it like this:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">var</span> wg sync.WaitGroup

    RunConc(&amp;wg, work1, work2, work3)
    wg.Wait()
    fmt.Println(<span class="hljs-string">"all done"</span>)
}
</code></pre>
<p>This works, but the client still needs to create a wait group and call <code>Wait()</code>. We can improve this by hiding the wait group inside the function:</p>
<pre><code class="lang-go"><span class="hljs-comment">// RunConc executes functions concurrently and waits for them to finish.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">RunConc</span><span class="hljs-params">(funcs ...<span class="hljs-keyword">func</span>()</span>)</span> {
    <span class="hljs-keyword">var</span> wg sync.WaitGroup
    wg.Add(<span class="hljs-built_in">len</span>(funcs))
    <span class="hljs-keyword">for</span> _, fn := <span class="hljs-keyword">range</span> funcs {
        <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
            <span class="hljs-keyword">defer</span> wg.Done()
            fn()
        }()
    }
    wg.Wait()
}
</code></pre>
<p>Now the client code is much simpler:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    RunConc(work1, work2, work3)
    fmt.Println(<span class="hljs-string">"all done"</span>)
}
</code></pre>
<h3 id="heading-encapsulated-types">Encapsulated Types</h3>
<p>For more complex scenarios, you can create a type that encapsulates the wait group:</p>
<pre><code class="lang-go"><span class="hljs-comment">// ConcurrentGroup runs functions concurrently.</span>
<span class="hljs-keyword">type</span> ConcurrentGroup <span class="hljs-keyword">struct</span> {
    wg sync.WaitGroup
}

<span class="hljs-comment">// Run adds a function to the group and executes it in a goroutine.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(cg *ConcurrentGroup)</span> <span class="hljs-title">Run</span><span class="hljs-params">(fn <span class="hljs-keyword">func</span>()</span>)</span> {
    cg.wg.Add(<span class="hljs-number">1</span>)
    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">defer</span> cg.wg.Done()
        fn()
    }()
}

<span class="hljs-comment">// Wait blocks until all functions in the group have finished.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(cg *ConcurrentGroup)</span> <span class="hljs-title">Wait</span><span class="hljs-params">()</span></span> {
    cg.wg.Wait()
}
</code></pre>
<p>The client code becomes:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">var</span> cg ConcurrentGroup

    cg.Run(work1)
    cg.Run(work2)
    cg.Run(work3)
    cg.Wait()
    fmt.Println(<span class="hljs-string">"all done"</span>)
}
</code></pre>
<p>In rare cases, a client may want to explicitly access your code's synchronization machinery. But usually it's better to encapsulate the synchronization logic.</p>
<hr />
<h2 id="heading-add-after-wait">Add after Wait</h2>
<p>Normally, all <code>Add</code> calls happen before <code>Wait</code>. But technically, there's nothing stopping us from doing some of the <code>Add</code> calls before <code>Wait</code> and some after (from another goroutine).</p>
<p>Let's say we have a function <code>runWork</code> that does its job in a separate goroutine:</p>
<pre><code class="lang-go"><span class="hljs-comment">// runWork performs work in a goroutine.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">runWork</span><span class="hljs-params">(wg *sync.WaitGroup)</span></span> {
    wg.Add(<span class="hljs-number">1</span>)
    fmt.Println(<span class="hljs-string">"starting work..."</span>)
    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        time.Sleep(<span class="hljs-number">50</span> * time.Millisecond)
        fmt.Println(<span class="hljs-string">"work done"</span>)
        wg.Done()
    }()
}
</code></pre>
<p>We'll do the following:</p>
<ul>
<li>Start a <code>runWork</code> goroutine (worker);</li>
<li>Start another goroutine to wait for the work to finish (waiter);</li>
<li>Start two more workers;</li>
<li>When all three workers have finished, the waiter will wake up and signal completion to the <code>main</code> function.</li>
</ul>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-comment">// main wait group</span>
    <span class="hljs-keyword">var</span> wgMain sync.WaitGroup

    <span class="hljs-comment">// worker wait group</span>
    <span class="hljs-keyword">var</span> wgWork sync.WaitGroup

    <span class="hljs-comment">// run the first worker</span>
    runWork(&amp;wgWork)

    <span class="hljs-comment">// the waiter goroutine waits for all workers to finish,</span>
    <span class="hljs-comment">// and then completes the main wait group</span>
    wgMain.Add(<span class="hljs-number">1</span>)
    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        fmt.Println(<span class="hljs-string">"waiting for work to be done..."</span>)
        wgWork.Wait()
        fmt.Println(<span class="hljs-string">"all work done"</span>)
        wgMain.Done()
    }()

    <span class="hljs-comment">// run two more workers after a while</span>
    time.Sleep(<span class="hljs-number">10</span> * time.Millisecond)
    runWork(&amp;wgWork)
    runWork(&amp;wgWork)

    <span class="hljs-comment">// executes when the waiter goroutine finishes</span>
    wgMain.Wait()
}
</code></pre>
<pre><code>starting work...
waiting <span class="hljs-keyword">for</span> work to be done...
starting work...
starting work...
work done
work done
work done
all work done
</code></pre><p>This is rarely used in practice.</p>
<hr />
<h2 id="heading-multiple-waits">Multiple Waits</h2>
<p>Another not-so-popular <code>WaitGroup</code> feature: you can call <code>Wait</code> from multiple goroutines. They will all block until the group's counter reaches zero.</p>
<p>For example, we can start one worker and three waiters:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">var</span> wg sync.WaitGroup

    <span class="hljs-comment">// worker</span>
    wg.Add(<span class="hljs-number">1</span>)
    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-comment">// do stuff</span>
        time.Sleep(<span class="hljs-number">50</span> * time.Millisecond)
        fmt.Println(<span class="hljs-string">"work done"</span>)
        wg.Done()
    }()

    <span class="hljs-comment">// first waiter</span>
    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        wg.Wait()
        fmt.Println(<span class="hljs-string">"waiter 1 done"</span>)
    }()

    <span class="hljs-comment">// second waiter</span>
    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        wg.Wait()
        fmt.Println(<span class="hljs-string">"waiter 2 done"</span>)
    }()

    <span class="hljs-comment">// main waiter</span>
    wg.Wait()
    fmt.Println(<span class="hljs-string">"main waiter done"</span>)
}
</code></pre>
<pre><code>work done
waiter <span class="hljs-number">1</span> done
waiter <span class="hljs-number">2</span> done
main waiter done
</code></pre><p>All waiters unblock after the worker calls <code>wg.Done()</code>. But the order in which this happens is not guaranteed. Could be this:</p>
<pre><code>work done
waiter <span class="hljs-number">1</span> done
waiter <span class="hljs-number">2</span> done
main waiter done
</code></pre><p>Or this:</p>
<pre><code>work done
waiter <span class="hljs-number">1</span> done
main waiter done
waiter <span class="hljs-number">2</span> done
</code></pre><p>Or even this:</p>
<pre><code>work done
main waiter done
</code></pre><p>In the last case, the main waiter finished first, and then <code>main</code> exited before the other waiters could even print anything.</p>
<p>We'll see another use case for multiple <code>Wait</code>s in the chapter on semaphores.</p>
<hr />
<h2 id="heading-panic">Panic</h2>
<p>If multiple goroutines are involved in the wait group, there are multiple possible panic sources.</p>
<p>Let's say there's a <code>work</code> function that panics on even numbers:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">work</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">if</span> n := rand.IntN(<span class="hljs-number">9</span>) + <span class="hljs-number">1</span>; n%<span class="hljs-number">2</span> == <span class="hljs-number">0</span> {
        <span class="hljs-built_in">panic</span>(fmt.Errorf(<span class="hljs-string">"bad number: %d"</span>, n))
    }
    <span class="hljs-comment">// do stuff</span>
}
</code></pre>
<p>We start four <code>work</code> goroutines:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">var</span> wg sync.WaitGroup

    <span class="hljs-keyword">for</span> <span class="hljs-keyword">range</span> <span class="hljs-number">4</span> {
        wg.Go(work)
    }

    wg.Wait()
    fmt.Println(<span class="hljs-string">"work done"</span>)
}
</code></pre>
<pre><code>panic: bad number: <span class="hljs-number">8</span>

goroutine <span class="hljs-number">9</span> [running]:
main.work()
    /sandbox/src/main.go:<span class="hljs-number">19</span> +<span class="hljs-number">0x76</span>
sync.(*WaitGroup).Go.func1()
    /usr/local/go/src/sync/waitgroup.go:<span class="hljs-number">239</span> +<span class="hljs-number">0x4a</span>
created by sync.(*WaitGroup).Go <span class="hljs-keyword">in</span> goroutine <span class="hljs-number">1</span>
    /usr/local/go/src/sync/waitgroup.go:<span class="hljs-number">237</span> +<span class="hljs-number">0x73</span> (exit status <span class="hljs-number">2</span>)
</code></pre><p>And we face a panic (unless we are very lucky).</p>
<h3 id="heading-shared-recover">Shared Recover</h3>
<p>Let's add <code>recover</code> to catch the panic and run the program again:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">defer</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        val := <span class="hljs-built_in">recover</span>()
        <span class="hljs-keyword">if</span> val == <span class="hljs-literal">nil</span> {
            fmt.Println(<span class="hljs-string">"work done"</span>)
        } <span class="hljs-keyword">else</span> {
            fmt.Println(<span class="hljs-string">"panicked!"</span>)
        }
    }()

    <span class="hljs-keyword">var</span> wg sync.WaitGroup
    <span class="hljs-keyword">for</span> <span class="hljs-keyword">range</span> <span class="hljs-number">4</span> {
        wg.Go(work)
    }
    wg.Wait()
}
</code></pre>
<pre><code>panic: bad number: <span class="hljs-number">6</span>

goroutine <span class="hljs-number">10</span> [running]:
main.work()
    /sandbox/src/main.go:<span class="hljs-number">19</span> +<span class="hljs-number">0x76</span>
sync.(*WaitGroup).Go.func1()
    /usr/local/go/src/sync/waitgroup.go:<span class="hljs-number">239</span> +<span class="hljs-number">0x4a</span>
created by sync.(*WaitGroup).Go <span class="hljs-keyword">in</span> goroutine <span class="hljs-number">1</span>
    /usr/local/go/src/sync/waitgroup.go:<span class="hljs-number">237</span> +<span class="hljs-number">0x73</span> (exit status <span class="hljs-number">2</span>)
</code></pre><p>Nope. You might expect <code>recover</code> to catch the panic and print "panicked". But instead we get the same unhandled panic as before.</p>
<p>The problem is that <code>recover</code> has an important limitation: it only works within the <em>same goroutine</em> that caused the panic. In our case, the panic comes from the <code>work</code> goroutines, while <code>recover</code> runs in the <code>main</code> goroutine — so it doesn't catch the panic. Goroutines are completely independent, remember? You can only catch the panic happening in those goroutines themselves.</p>
<h3 id="heading-per-goroutine-recover">Per-Goroutine Recover</h3>
<p>Let's move <code>recover</code> inside the <code>work</code> goroutines:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">var</span> wg sync.WaitGroup
    panicked := <span class="hljs-literal">false</span>

    catchPanic := <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        err := <span class="hljs-built_in">recover</span>()
        <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
            panicked = <span class="hljs-literal">true</span>
        }
    }

    <span class="hljs-keyword">for</span> <span class="hljs-keyword">range</span> <span class="hljs-number">4</span> {
        wg.Go(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
            <span class="hljs-keyword">defer</span> catchPanic()
            work()
        })
    }

    wg.Wait()
    <span class="hljs-keyword">if</span> !panicked {
        fmt.Println(<span class="hljs-string">"work done"</span>)
    } <span class="hljs-keyword">else</span> {
        fmt.Println(<span class="hljs-string">"panicked!"</span>)
    }
}
</code></pre>
<pre><code>panicked!
</code></pre><p>Now, the panic is caught in its own goroutine, which then sets the <code>panicked</code> flag in the <code>main</code> goroutine. Now the program works fine and prints "panicked" as we expected.</p>
<blockquote>
<p>Here we are modifying the shared <code>panicked</code> variable from multiple goroutines. In general, this is not a good practice because it leads to data races (we'll talk about them in the next chapter). But in this particular case, there's no real harm from races.</p>
</blockquote>
<p>Key takeaway: you cannot catch a panic from "child" goroutines in the "parent" goroutine. If you want to catch a panic, do it in the goroutine where it happens.</p>
<hr />
<h2 id="heading-summary">Summary</h2>
<p>The wait group is used to wait for goroutines to finish. Now you understand how it works and how to apply it. Key points to remember:</p>
<ul>
<li><strong>Wait groups use an internal counter</strong> to track goroutines, not direct references to them.</li>
<li><strong>Always pass wait groups as pointers</strong> to ensure proper synchronization.</li>
<li><strong>Encapsulate wait groups</strong> in functions or types to hide implementation details from clients.</li>
<li><strong>Panics must be recovered</strong> in the same goroutine where they occur.</li>
<li><strong>Multiple waits are possible</strong> but execution order is not guaranteed.</li>
</ul>
<p>Wait groups provide a simple and efficient way to synchronize goroutines in concurrent Go programs.</p>
]]></content:encoded></item><item><title><![CDATA[Synchronizing Multiple Goroutines in Go: Four Key Approaches]]></title><description><![CDATA[In Go, the main goroutine often needs to wait for other goroutines to finish their tasks before continuing execution or exiting the program. This is a common requirement for concurrent synchronization. Go provides several mechanisms to achieve this, ...]]></description><link>https://blog.fshtab.com/waiting-for-multiple-goroutines-in-go</link><guid isPermaLink="true">https://blog.fshtab.com/waiting-for-multiple-goroutines-in-go</guid><dc:creator><![CDATA[Fedor S]]></dc:creator><pubDate>Wed, 29 Nov 2023 16:42:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1766406520152/131efa4a-8c59-4de8-9f63-efa1617df59f.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In Go, the main goroutine often needs to wait for other goroutines to finish their tasks before continuing execution or exiting the program. This is a common requirement for concurrent synchronization. Go provides several mechanisms to achieve this, depending on the scenario and requirements.</p>
<hr />
<h2 id="heading-method-1-using-syncwaitgroup">Method 1: Using sync.WaitGroup</h2>
<p><code>sync.WaitGroup</code> is the most commonly used synchronization tool in Go, designed to wait for a group of goroutines to finish their tasks. It works through a counter mechanism and is especially suitable when the main goroutine needs to wait for multiple sub-goroutines.</p>
<h3 id="heading-example-code">Example Code</h3>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"fmt"</span>
    <span class="hljs-string">"sync"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">var</span> wg sync.WaitGroup

    <span class="hljs-comment">// Start 3 goroutines</span>
    <span class="hljs-keyword">for</span> i := <span class="hljs-number">1</span>; i &lt;= <span class="hljs-number">3</span>; i++ {
        wg.Add(<span class="hljs-number">1</span>) <span class="hljs-comment">// Increment the counter by 1</span>
        <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(id <span class="hljs-keyword">int</span>)</span></span> {
            <span class="hljs-keyword">defer</span> wg.Done() <span class="hljs-comment">// Decrement the counter by 1 when the task is done</span>
            fmt.Printf(<span class="hljs-string">"Goroutine %d is running\n"</span>, id)
        }(i)
    }

    wg.Wait() <span class="hljs-comment">// Main goroutine waits for all goroutines to finish</span>
    fmt.Println(<span class="hljs-string">"All goroutines finished"</span>)
}
</code></pre>
<p>Output (order may vary):</p>
<pre><code class="lang-shell">Goroutine 1 is running
Goroutine 2 is running
Goroutine 3 is running
All goroutines finished
</code></pre>
<p>How it works:</p>
<ol>
<li><code>wg.Add(n)</code>: Increases the counter to indicate the number of goroutines to wait for.</li>
<li><code>wg.Done()</code>: Called by each goroutine upon completion, decreases the counter by 1.</li>
<li><code>wg.Wait()</code>: Blocks the main goroutine until the counter reaches zero.</li>
</ol>
<p><strong>Advantages:</strong></p>
<ul>
<li>Simple and easy to use, suitable for a fixed number of goroutines.</li>
<li>No need for additional channels, low performance overhead.</li>
</ul>
<hr />
<h2 id="heading-method-2-using-channel">Method 2: Using Channel</h2>
<p>By passing signals through channels, the main goroutine can wait until all other goroutines have sent completion signals. This method is more flexible, but usually a bit more complex than WaitGroup.</p>
<h3 id="heading-example-code-1">Example Code</h3>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> <span class="hljs-string">"fmt"</span>

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    done := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">struct</span>{}) <span class="hljs-comment">// A signal channel to notify completion</span>
    numGoroutines := <span class="hljs-number">3</span>

    <span class="hljs-keyword">for</span> i := <span class="hljs-number">1</span>; i &lt;= numGoroutines; i++ {
        <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(id <span class="hljs-keyword">int</span>)</span></span> {
            fmt.Printf(<span class="hljs-string">"Goroutine %d is running\n"</span>, id)
            done &lt;- <span class="hljs-keyword">struct</span>{}{} <span class="hljs-comment">// Send a signal when the task is done</span>
        }(i)
    }

    <span class="hljs-comment">// Wait for all goroutines to finish</span>
    <span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i &lt; numGoroutines; i++ {
        &lt;-done <span class="hljs-comment">// Receive signals</span>
    }
    fmt.Println(<span class="hljs-string">"All goroutines finished"</span>)
}
</code></pre>
<p>Output (order may vary):</p>
<pre><code class="lang-shell">Goroutine 1 is running
Goroutine 2 is running
Goroutine 3 is running
All goroutines finished
</code></pre>
<p>How it works:</p>
<ol>
<li>Each goroutine sends a signal to the <code>done</code> channel upon completion.</li>
<li>The main goroutine confirms that all tasks are done by receiving the specified number of signals.</li>
</ol>
<p><strong>Advantages:</strong></p>
<ul>
<li>High flexibility, can carry data (such as task results).</li>
<li>Suitable for a dynamic number of goroutines.</li>
</ul>
<p><strong>Disadvantages:</strong></p>
<ul>
<li>Need to manually manage the number of receives, which can make the code a bit cumbersome.</li>
</ul>
<hr />
<h2 id="heading-method-3-controlling-exit-with-context">Method 3: Controlling Exit with Context</h2>
<p>Using <code>context.Context</code> allows you to gracefully control goroutine exits and have the main goroutine wait until all tasks are done. This method is especially useful in scenarios requiring cancellation or timeouts.</p>
<h3 id="heading-example-code-2">Example Code</h3>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"context"</span>
    <span class="hljs-string">"fmt"</span>
    <span class="hljs-string">"sync"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    ctx, cancel := context.WithCancel(context.Background())
    <span class="hljs-keyword">var</span> wg sync.WaitGroup

    <span class="hljs-keyword">for</span> i := <span class="hljs-number">1</span>; i &lt;= <span class="hljs-number">3</span>; i++ {
        wg.Add(<span class="hljs-number">1</span>)
        <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(id <span class="hljs-keyword">int</span>)</span></span> {
            <span class="hljs-keyword">defer</span> wg.Done()
            <span class="hljs-keyword">select</span> {
            <span class="hljs-keyword">case</span> &lt;-ctx.Done():
                fmt.Printf(<span class="hljs-string">"Goroutine %d cancelled\n"</span>, id)
                <span class="hljs-keyword">return</span>
            <span class="hljs-keyword">default</span>:
                fmt.Printf(<span class="hljs-string">"Goroutine %d is running\n"</span>, id)
            }
        }(i)
    }

    <span class="hljs-comment">// Simulate task completion</span>
    cancel()       <span class="hljs-comment">// Send cancel signal</span>
    wg.Wait()      <span class="hljs-comment">// Wait for all goroutines to exit</span>
    fmt.Println(<span class="hljs-string">"All goroutines finished"</span>)
}
</code></pre>
<p>Output (may vary depending on when cancellation occurs):</p>
<pre><code class="lang-shell">Goroutine 1 is running
Goroutine 2 is running
Goroutine 3 is running
All goroutines finished
</code></pre>
<p>How it works:</p>
<ol>
<li>The context is used to notify goroutines to exit.</li>
<li>WaitGroup ensures that the main goroutine waits for all goroutines to complete.</li>
</ol>
<p><strong>Advantages:</strong></p>
<ul>
<li>Supports cancellation and timeout, suitable for complex concurrent scenarios.</li>
</ul>
<p><strong>Disadvantages:</strong></p>
<ul>
<li>Slightly more complex code.</li>
</ul>
<hr />
<h2 id="heading-method-4-using-errgroup-recommended">Method 4: Using errgroup (Recommended)</h2>
<p><code>golang.org/x/sync/errgroup</code> is an advanced tool that combines the waiting functionality of WaitGroup with error handling, making it especially suitable for waiting for a group of tasks and handling errors.</p>
<h3 id="heading-example-code-3">Example Code</h3>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"fmt"</span>
    <span class="hljs-string">"golang.org/x/sync/errgroup"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">var</span> g errgroup.Group

    <span class="hljs-keyword">for</span> i := <span class="hljs-number">1</span>; i &lt;= <span class="hljs-number">3</span>; i++ {
        id := i
        g.Go(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span> <span class="hljs-title">error</span></span> {
            fmt.Printf(<span class="hljs-string">"Goroutine %d is running\n"</span>, id)
            <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span> <span class="hljs-comment">// No error</span>
        })
    }

    <span class="hljs-keyword">if</span> err := g.Wait(); err != <span class="hljs-literal">nil</span> {
        fmt.Println(<span class="hljs-string">"Error:"</span>, err)
    } <span class="hljs-keyword">else</span> {
        fmt.Println(<span class="hljs-string">"All goroutines finished"</span>)
    }
}
</code></pre>
<p>Output:</p>
<pre><code class="lang-shell">Goroutine 1 is running
Goroutine 2 is running
Goroutine 3 is running
All goroutines finished
</code></pre>
<p>How it works:</p>
<ol>
<li><code>g.Go()</code> starts a goroutine and adds it to the group.</li>
<li><code>g.Wait()</code> waits for all goroutines to finish and returns the first non-nil error (if any).</li>
</ol>
<p><strong>Advantages:</strong></p>
<ul>
<li>Simple and elegant, supports error propagation.</li>
<li>Built-in context support (can use <code>errgroup.WithContext</code>).</li>
</ul>
<p><strong>Installation:</strong></p>
<ul>
<li>Requires <code>go get golang.org/x/sync/errgroup</code>.</li>
</ul>
<hr />
<h2 id="heading-which-method-to-choose">Which Method to Choose?</h2>
<h3 id="heading-syncwaitgroup">sync.WaitGroup</h3>
<ul>
<li><strong>Applicable scenarios:</strong> Simple tasks with a fixed number.</li>
<li><strong>Advantages:</strong> Simple and efficient.</li>
<li><strong>Disadvantages:</strong> Does not support error handling or cancellation.</li>
</ul>
<h3 id="heading-channel">Channel</h3>
<ul>
<li><strong>Applicable scenarios:</strong> Dynamic tasks or when results need to be passed.</li>
<li><strong>Advantages:</strong> Highly flexible.</li>
<li><strong>Disadvantages:</strong> Manual management is more complex.</li>
</ul>
<h3 id="heading-context">context</h3>
<ul>
<li><strong>Applicable scenarios:</strong> Complex situations where cancellation or timeout is required.</li>
<li><strong>Advantages:</strong> Supports cancellation and timeout.</li>
<li><strong>Disadvantages:</strong> Code is slightly more complex.</li>
</ul>
<h3 id="heading-errgroup">errgroup</h3>
<ul>
<li><strong>Applicable scenarios:</strong> Modern applications that require error handling and waiting.</li>
<li><strong>Advantages:</strong> Elegant and powerful.</li>
<li><strong>Disadvantages:</strong> Requires extra dependency.</li>
</ul>
<hr />
<h2 id="heading-others-why-doesnt-the-main-goroutine-just-sleep">Others: Why Doesn't the Main Goroutine Just Sleep?</h2>
<p><code>time.Sleep</code> only introduces a fixed delay and cannot accurately wait for tasks to finish. This may cause the program to exit prematurely or lead to unnecessary waiting. Synchronization tools are more reliable.</p>
<hr />
<h2 id="heading-summary">Summary</h2>
<p>The most commonly used method for the main goroutine to wait for other goroutines is <code>sync.WaitGroup</code>, which is simple and efficient. If you need error handling or cancellation capabilities, <code>errgroup</code> or a combination with <code>context</code> is recommended. Choose the appropriate tool according to your specific requirements to ensure clear program logic and prevent resource leaks.</p>
]]></content:encoded></item><item><title><![CDATA[Go: Understanding Race Conditions and Atomic Synchronization]]></title><description><![CDATA[Preventing data races with mutexes may sound easy, but dealing with race conditions is a whole other matter. Let's learn how to handle these beasts!

Race Condition
Let's say we're keeping track of the money in users' accounts:
// Accounts - money in...]]></description><link>https://blog.fshtab.com/go-race-conditions-atomic-operations</link><guid isPermaLink="true">https://blog.fshtab.com/go-race-conditions-atomic-operations</guid><dc:creator><![CDATA[Fedor S]]></dc:creator><pubDate>Thu, 05 Oct 2023 17:33:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1766406549310/2336784b-6480-42c5-91f1-9932e1491e10.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Preventing data races with mutexes may sound easy, but dealing with race conditions is a whole other matter. Let's learn how to handle these beasts!</p>
<hr />
<h2 id="heading-race-condition">Race Condition</h2>
<p>Let's say we're keeping track of the money in users' accounts:</p>
<pre><code class="lang-go"><span class="hljs-comment">// Accounts - money in users' accounts.</span>
<span class="hljs-keyword">type</span> Accounts <span class="hljs-keyword">struct</span> {
    bal <span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]<span class="hljs-keyword">int</span>
    mu  sync.Mutex
}

<span class="hljs-comment">// NewAccounts creates a new set of accounts.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">NewAccounts</span><span class="hljs-params">(bal <span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]<span class="hljs-keyword">int</span>)</span> *<span class="hljs-title">Accounts</span></span> {
    <span class="hljs-keyword">return</span> &amp;Accounts{bal: maps.Clone(bal)}
}
</code></pre>
<p>We can check the balance by username or change the balance:</p>
<pre><code class="lang-go"><span class="hljs-comment">// Get returns the user's balance.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(a *Accounts)</span> <span class="hljs-title">Get</span><span class="hljs-params">(name <span class="hljs-keyword">string</span>)</span> <span class="hljs-title">int</span></span> {
    a.mu.Lock()
    <span class="hljs-keyword">defer</span> a.mu.Unlock()
    <span class="hljs-keyword">return</span> a.bal[name]
}

<span class="hljs-comment">// Set changes the user's balance.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(a *Accounts)</span> <span class="hljs-title">Set</span><span class="hljs-params">(name <span class="hljs-keyword">string</span>, amount <span class="hljs-keyword">int</span>)</span></span> {
    a.mu.Lock()
    <span class="hljs-keyword">defer</span> a.mu.Unlock()
    a.bal[name] = amount
}
</code></pre>
<p>Account operations — <code>Get</code> and <code>Set</code> — are concurrent-safe, thanks to the mutex.</p>
<p>There's also a store that sells Lego sets:</p>
<pre><code class="lang-go"><span class="hljs-comment">// A Lego set.</span>
<span class="hljs-keyword">type</span> LegoSet <span class="hljs-keyword">struct</span> {
    name  <span class="hljs-keyword">string</span>
    price <span class="hljs-keyword">int</span>
}
</code></pre>
<p>Alice has 50 coins in her account. She wants to buy two sets: "Castle" for 40 coins and "Plants" for 20 coins:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    acc := NewAccounts(<span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]<span class="hljs-keyword">int</span>{
        <span class="hljs-string">"alice"</span>: <span class="hljs-number">50</span>,
    })
    castle := LegoSet{name: <span class="hljs-string">"Castle"</span>, price: <span class="hljs-number">40</span>}
    plants := LegoSet{name: <span class="hljs-string">"Plants"</span>, price: <span class="hljs-number">20</span>}

    <span class="hljs-keyword">var</span> wg sync.WaitGroup

    <span class="hljs-comment">// Alice buys a castle.</span>
    wg.Go(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        balance := acc.Get(<span class="hljs-string">"alice"</span>)
        <span class="hljs-keyword">if</span> balance &lt; castle.price {
            <span class="hljs-keyword">return</span>
        }
        time.Sleep(<span class="hljs-number">5</span> * time.Millisecond)
        acc.Set(<span class="hljs-string">"alice"</span>, balance-castle.price)
        fmt.Println(<span class="hljs-string">"Alice bought the castle"</span>)
    })

    <span class="hljs-comment">// Alice buys plants.</span>
    wg.Go(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        balance := acc.Get(<span class="hljs-string">"alice"</span>)
        <span class="hljs-keyword">if</span> balance &lt; plants.price {
            <span class="hljs-keyword">return</span>
        }
        time.Sleep(<span class="hljs-number">10</span> * time.Millisecond)
        acc.Set(<span class="hljs-string">"alice"</span>, balance-plants.price)
        fmt.Println(<span class="hljs-string">"Alice bought the plants"</span>)
    })

    wg.Wait()

    balance := acc.Get(<span class="hljs-string">"alice"</span>)
    fmt.Println(<span class="hljs-string">"Alice's balance:"</span>, balance)
}
</code></pre>
<pre><code>Alice bought the castle
Alice bought the plants
Alice<span class="hljs-string">'s balance: 30</span>
</code></pre><p>What a twist! Not only did Alice buy both sets for a total of 60 coins (even though she only had 50 coins), but she also ended up with 30 coins left! Great deal for Alice, not so great for us.</p>
<p>The problem is that checking and updating the balance is not an atomic operation:</p>
<pre><code class="lang-go"><span class="hljs-comment">// body of the second goroutine</span>
balance := acc.Get(<span class="hljs-string">"alice"</span>)             <span class="hljs-comment">// (1)</span>
<span class="hljs-keyword">if</span> balance &lt; plants.price {             <span class="hljs-comment">// (2)</span>
    <span class="hljs-keyword">return</span>
}
time.Sleep(<span class="hljs-number">10</span> * time.Millisecond)
acc.Set(<span class="hljs-string">"alice"</span>, balance-plants.price)  <span class="hljs-comment">// (3)</span>
</code></pre>
<p>At point ➊, we see a balance of 50 coins (the first goroutine hasn't done anything yet), so the check at ➋ passes. By point ➌, Alice has already bought the castle (the first goroutine has finished), so her actual balance is 10 coins. But we don't know this and still think her balance is 50 coins. So at point ➌, Alice buys the plants for 20 coins, and the balance becomes 30 coins (the "assumed" balance of 50 coins minus the 20 coins for the plants = 30 coins).</p>
<p>Individual actions on the balance are safe (there's no <em>data race</em>). However, balance reads/writes from different goroutines can get "mixed up", leading to an incorrect final balance. This situation is called a <em>race condition</em>.</p>
<p>You can't fully eliminate uncertainty in a concurrent environment. Events will happen in an unpredictable order — that's just how concurrency works. However, you can protect the system's state — in our case, the purchased sets and balance — so it stays correct no matter what order things happen in.</p>
<p>Let's check and update the balance in one atomic operation, protecting the entire purchase with a mutex. This way, purchases are processed strictly sequentially:</p>
<pre><code class="lang-go"><span class="hljs-comment">// Shared mutex.</span>
<span class="hljs-keyword">var</span> mu sync.Mutex

<span class="hljs-comment">// Alice buys a castle.</span>
wg.Go(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
    <span class="hljs-comment">// Protect the entire purchase with a mutex.</span>
    mu.Lock()
    <span class="hljs-keyword">defer</span> mu.Unlock()

    balance := acc.Get(<span class="hljs-string">"alice"</span>)
    <span class="hljs-keyword">if</span> balance &lt; castle.price {
        <span class="hljs-keyword">return</span>
    }
    time.Sleep(<span class="hljs-number">5</span> * time.Millisecond)
    acc.Set(<span class="hljs-string">"alice"</span>, balance-castle.price)
    fmt.Println(<span class="hljs-string">"Alice bought the castle"</span>)
})

<span class="hljs-comment">// Alice buys plants.</span>
wg.Go(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
    <span class="hljs-comment">// Protect the entire purchase with a mutex.</span>
    mu.Lock()
    <span class="hljs-keyword">defer</span> mu.Unlock()

    balance := acc.Get(<span class="hljs-string">"alice"</span>)
    <span class="hljs-keyword">if</span> balance &lt; plants.price {
        <span class="hljs-keyword">return</span>
    }
    time.Sleep(<span class="hljs-number">10</span> * time.Millisecond)
    acc.Set(<span class="hljs-string">"alice"</span>, balance-plants.price)
    fmt.Println(<span class="hljs-string">"Alice bought the plants"</span>)
})
</code></pre>
<pre><code>Alice bought the plants
Alice<span class="hljs-string">'s balance: 30</span>
</code></pre><p>One of the goroutines will run first, lock the mutex, check and update the balance, then unlock the mutex. Only after that will the second goroutine be able to lock the mutex and make its purchase.</p>
<p>We still can't be sure which purchase will happen — it depends on the order the goroutines run. But now we are certain that Alice won't buy more than she's supposed to, and the final balance will be correct:</p>
<pre><code>Alice bought the castle
Alice<span class="hljs-string">'s balance: 10</span>
</code></pre><p>Or:</p>
<pre><code>Alice bought the plants
Alice<span class="hljs-string">'s balance: 30</span>
</code></pre><p>To reiterate:</p>
<ul>
<li>A <em>data race</em> happens when multiple goroutines access shared data, and at least one of them modifies it. We need to protect the data from this kind of concurrent access.</li>
<li>A <em>race condition</em> happens when an unpredictable order of operations leads to an incorrect system state. In a concurrent environment, we can't control the exact order things happen. Still, we need to make sure that no matter the order, the system always ends up in the correct state.</li>
</ul>
<p>Go's race detector can find data races, but it doesn't catch race conditions. It's always up to the programmer to prevent race conditions.</p>
<hr />
<h2 id="heading-compare-and-set">Compare-and-Set</h2>
<p>Let's go back to the situation with the race condition before we added the mutex:</p>
<pre><code class="lang-go"><span class="hljs-comment">// Alice's balance = 50 coins.</span>
<span class="hljs-comment">// Castle price = 40 coins.</span>
<span class="hljs-comment">// Plants price = 20 coins.</span>

<span class="hljs-comment">// Alice buys a castle.</span>
wg.Go(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
    balance := acc.Get(<span class="hljs-string">"alice"</span>)
    <span class="hljs-keyword">if</span> balance &lt; castle.price {
        <span class="hljs-keyword">return</span>
    }
    time.Sleep(<span class="hljs-number">5</span> * time.Millisecond)
    acc.Set(<span class="hljs-string">"alice"</span>, balance-castle.price)
    fmt.Println(<span class="hljs-string">"Alice bought the castle"</span>)
})

<span class="hljs-comment">// Alice buys plants.</span>
wg.Go(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
    balance := acc.Get(<span class="hljs-string">"alice"</span>)
    <span class="hljs-keyword">if</span> balance &lt; plants.price {
        <span class="hljs-keyword">return</span>
    }
    time.Sleep(<span class="hljs-number">10</span> * time.Millisecond)
    acc.Set(<span class="hljs-string">"alice"</span>, balance-plants.price)
    fmt.Println(<span class="hljs-string">"Alice bought the plants"</span>)
})
</code></pre>
<pre><code>Alice bought the castle
Alice bought the plants
Alice<span class="hljs-string">'s balance: 30</span>
</code></pre><p>As we discussed, the reason for the incorrect final state is that buying a set (checking and updating the balance) is not an atomic operation:</p>
<pre><code class="lang-go"><span class="hljs-comment">// body of the second goroutine</span>
balance := acc.Get(<span class="hljs-string">"alice"</span>)             <span class="hljs-comment">// (1)</span>
<span class="hljs-keyword">if</span> balance &lt; plants.price {             <span class="hljs-comment">// (2)</span>
    <span class="hljs-keyword">return</span>
}
time.Sleep(<span class="hljs-number">10</span> * time.Millisecond)
acc.Set(<span class="hljs-string">"alice"</span>, balance-plants.price)  <span class="hljs-comment">// (3)</span>
</code></pre>
<p>At point ➊, we see a balance of 50 coins, so the check at ➋ passes. By point ➌, Alice has already bought the castle, so her actual balance is 10 coins. But we don't know this and still think her balance is 50 coins. So at point ➌, Alice buys the plants for 20 coins, and the balance becomes 30 coins (the "assumed" balance of 50 coins minus the 20 coins for the plants = 30 coins).</p>
<p>To solve the problem, we can protect the entire purchase with a mutex, just like we did before. But there's another way to handle it.</p>
<p>We can keep two separate operations (checking and updating the balance), but make sure they happen atomically. To do this, we'll use a <em>compare-and-set</em> pattern:</p>
<pre><code class="lang-go"><span class="hljs-comment">// Buy attempts to purchase a set for the given buyer.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(a *Accounts)</span> <span class="hljs-title">Buy</span><span class="hljs-params">(buyer <span class="hljs-keyword">string</span>, price <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">bool</span></span> {
    a.mu.Lock()
    <span class="hljs-keyword">defer</span> a.mu.Unlock()

    balance := a.bal[buyer]
    <span class="hljs-keyword">if</span> balance &lt; price {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>
    }
    a.bal[buyer] = balance - price
    <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>
}
</code></pre>
<p>Now the purchase logic is atomic — checking and updating happen together, protected by the mutex. The client code becomes simpler:</p>
<pre><code class="lang-go"><span class="hljs-comment">// Alice buys a castle.</span>
wg.Go(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">if</span> acc.Buy(<span class="hljs-string">"alice"</span>, castle.price) {
        fmt.Println(<span class="hljs-string">"Alice bought the castle"</span>)
    }
})

<span class="hljs-comment">// Alice buys plants.</span>
wg.Go(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">if</span> acc.Buy(<span class="hljs-string">"alice"</span>, plants.price) {
        fmt.Println(<span class="hljs-string">"Alice bought the plants"</span>)
    }
})
</code></pre>
<pre><code>Alice bought the castle
Alice<span class="hljs-string">'s balance: 10</span>
</code></pre><p>Now only one purchase can succeed, and the final balance will always be correct.</p>
<hr />
<h2 id="heading-idempotence-and-atomicity">Idempotence and Atomicity</h2>
<p>An operation is <em>idempotent</em> if performing it multiple times has the same effect as performing it once. For example, setting a value is idempotent:</p>
<pre><code class="lang-go">acc.Set(<span class="hljs-string">"alice"</span>, <span class="hljs-number">30</span>)
acc.Set(<span class="hljs-string">"alice"</span>, <span class="hljs-number">30</span>)  <span class="hljs-comment">// Same effect as the first call</span>
</code></pre>
<p>But incrementing a value is not idempotent:</p>
<pre><code class="lang-go">acc.Set(<span class="hljs-string">"alice"</span>, acc.Get(<span class="hljs-string">"alice"</span>) + <span class="hljs-number">10</span>)
acc.Set(<span class="hljs-string">"alice"</span>, acc.Get(<span class="hljs-string">"alice"</span>) + <span class="hljs-number">10</span>)  <span class="hljs-comment">// Different effect!</span>
</code></pre>
<p>An operation is <em>atomic</em> if it appears to happen all at once from the perspective of other goroutines. Even if the operation involves multiple steps internally, other goroutines can't see intermediate states.</p>
<p>In our purchase example, the <code>Buy</code> method is atomic because the entire check-and-update operation happens while holding the mutex. Other goroutines can't see the balance between the check and the update.</p>
<hr />
<h2 id="heading-locker">Locker</h2>
<p>The <code>sync.Locker</code> interface provides a standard way to work with locks:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> Locker <span class="hljs-keyword">interface</span> {
    Lock()
    Unlock()
}
</code></pre>
<p>Both <code>sync.Mutex</code> and <code>sync.RWMutex</code> implement this interface. This allows you to write code that works with any type of lock:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">doWork</span><span class="hljs-params">(lock sync.Locker)</span></span> {
    lock.Lock()
    <span class="hljs-keyword">defer</span> lock.Unlock()
    <span class="hljs-comment">// Do work...</span>
}
</code></pre>
<hr />
<h2 id="heading-trylock">TryLock</h2>
<p>Sometimes you want to try to acquire a lock, but if it's not available, you should immediately return an error instead of waiting.</p>
<p>We can use the <code>TryLock</code> method of a mutex to implement this logic:</p>
<pre><code class="lang-go"><span class="hljs-comment">// External is a client for an external system.</span>
<span class="hljs-keyword">type</span> External <span class="hljs-keyword">struct</span> {
    lock sync.Mutex
}

<span class="hljs-comment">// Call calls the external system.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(e *External)</span> <span class="hljs-title">Call</span><span class="hljs-params">()</span> <span class="hljs-title">error</span></span> {
    <span class="hljs-keyword">if</span> !e.lock.TryLock() {
        <span class="hljs-keyword">return</span> errors.New(<span class="hljs-string">"busy"</span>)  <span class="hljs-comment">// (1)</span>
    }
    <span class="hljs-keyword">defer</span> e.lock.Unlock()
    <span class="hljs-comment">// Simulate a remote call.</span>
    time.Sleep(<span class="hljs-number">100</span> * time.Millisecond)
    <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
}
</code></pre>
<p><code>TryLock</code> tries to lock the mutex, just like a regular <code>Lock</code>. But if it can't, it returns <code>false</code> right away instead of blocking the goroutine. This way, we can immediately return an error at ➊ instead of waiting for the system to become available.</p>
<p>Now, out of four simultaneous calls, only one will go through. The others will get a "busy" error:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">const</span> nCalls = <span class="hljs-number">4</span>
    ex := <span class="hljs-built_in">new</span>(External)
    start := time.Now()

    <span class="hljs-keyword">var</span> wg sync.WaitGroup
    <span class="hljs-keyword">for</span> <span class="hljs-keyword">range</span> nCalls {
        wg.Go(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
            err := ex.Call()
            <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
                fmt.Println(err)
            } <span class="hljs-keyword">else</span> {
                fmt.Println(<span class="hljs-string">"success"</span>)
            }
        })
    }
    wg.Wait()

    fmt.Printf(
        <span class="hljs-string">"%d calls took %d ms\n"</span>,
        nCalls, time.Since(start).Milliseconds(),
    )
}
</code></pre>
<pre><code>busy
busy
busy
success
<span class="hljs-number">4</span> calls took <span class="hljs-number">100</span> ms
</code></pre><p>According to the standard library docs, <code>TryLock</code> is rarely needed. In fact, using it might mean there's a problem with your program's design. For example, if you're calling <code>TryLock</code> in a busy-wait loop ("keep trying until the resource is free") — that's usually a bad sign:</p>
<pre><code class="lang-go"><span class="hljs-keyword">for</span> {
    <span class="hljs-keyword">if</span> mutex.TryLock() {
        <span class="hljs-comment">// Use the shared resource.</span>
        mutex.Unlock()
        <span class="hljs-keyword">break</span>
    }
}
</code></pre>
<p>This code will keep one CPU core at 100% usage until the mutex is unlocked. It's much better to use a regular <code>Lock</code> so the scheduler can take the blocked goroutine off the CPU.</p>
<hr />
<h2 id="heading-shared-nothing">Shared Nothing</h2>
<p>Let's go back one last time to Alice and the Lego sets we started the chapter with.</p>
<p>We manage user accounts:</p>
<pre><code class="lang-go"><span class="hljs-comment">// Accounts - money in users' accounts.</span>
<span class="hljs-keyword">type</span> Accounts <span class="hljs-keyword">struct</span> {
    bal <span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]<span class="hljs-keyword">int</span>
    mu  sync.Mutex
}

<span class="hljs-comment">// NewAccounts creates a new set of accounts.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">NewAccounts</span><span class="hljs-params">(bal <span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]<span class="hljs-keyword">int</span>)</span> *<span class="hljs-title">Accounts</span></span> {
    <span class="hljs-keyword">return</span> &amp;Accounts{bal: maps.Clone(bal)}
}

<span class="hljs-comment">// Get returns the user's balance.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(a *Accounts)</span> <span class="hljs-title">Get</span><span class="hljs-params">(name <span class="hljs-keyword">string</span>)</span> <span class="hljs-title">int</span></span> {
    a.mu.Lock()          <span class="hljs-comment">// (1)</span>
    <span class="hljs-keyword">defer</span> a.mu.Unlock()
    <span class="hljs-keyword">return</span> a.bal[name]
}

<span class="hljs-comment">// Set changes the user's balance.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(a *Accounts)</span> <span class="hljs-title">Set</span><span class="hljs-params">(name <span class="hljs-keyword">string</span>, amount <span class="hljs-keyword">int</span>)</span></span> {
    a.mu.Lock()          <span class="hljs-comment">// (2)</span>
    <span class="hljs-keyword">defer</span> a.mu.Unlock()
    a.bal[name] = amount
}
</code></pre>
<p>And handle purchases:</p>
<pre><code class="lang-go">acc := NewAccounts(<span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]<span class="hljs-keyword">int</span>{
    <span class="hljs-string">"alice"</span>: <span class="hljs-number">50</span>,
})
castle := LegoSet{name: <span class="hljs-string">"Castle"</span>, price: <span class="hljs-number">40</span>}
plants := LegoSet{name: <span class="hljs-string">"Plants"</span>, price: <span class="hljs-number">20</span>}

<span class="hljs-comment">// Shared mutex.</span>
<span class="hljs-keyword">var</span> mu sync.Mutex

<span class="hljs-comment">// Alice buys a castle.</span>
wg.Go(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
    <span class="hljs-comment">// Protect the entire purchase with a mutex.</span>
    mu.Lock()            <span class="hljs-comment">// (3)</span>
    <span class="hljs-keyword">defer</span> mu.Unlock()

    <span class="hljs-comment">// Check and update the balance.</span>
})

<span class="hljs-comment">// Alice buys plants.</span>
wg.Go(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
    <span class="hljs-comment">// Protect the entire purchase with a mutex.</span>
    mu.Lock()            <span class="hljs-comment">// (4)</span>
    <span class="hljs-keyword">defer</span> mu.Unlock()

    <span class="hljs-comment">// Check and update the balance.</span>
})
</code></pre>
<p>This isn't a very complex use case — I'm sure you've seen worse. Still, we had to put in some effort:</p>
<ul>
<li>Protect the balance with a mutex to prevent a data race ➊ ➋.</li>
<li>Protect the entire purchase operation with a mutex (or use compare-and-set) to make sure the final state is correct ➌ ➍.</li>
</ul>
<p>We were lucky to notice and prevent the race condition during a purchase. What if we had missed it?</p>
<p>There's another approach to achieving safe concurrency: instead of protecting shared state when working with multiple goroutines, we can avoid shared state altogether. Channels can help us do this.</p>
<p>Here's the idea: we'll create a <code>Processor</code> function that accepts purchase requests through an input channel, processes them, and sends the results back through an output channel:</p>
<pre><code class="lang-go"><span class="hljs-comment">// A purchase request.</span>
<span class="hljs-keyword">type</span> Request <span class="hljs-keyword">struct</span> {
    buyer <span class="hljs-keyword">string</span>
    set   LegoSet
}

<span class="hljs-comment">// A purchase result.</span>
<span class="hljs-keyword">type</span> Purchase <span class="hljs-keyword">struct</span> {
    buyer   <span class="hljs-keyword">string</span>
    set     LegoSet
    succeed <span class="hljs-keyword">bool</span>
    balance <span class="hljs-keyword">int</span> <span class="hljs-comment">// balance after purchase</span>
}

<span class="hljs-comment">// Processor handles purchases.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">Processor</span><span class="hljs-params">(acc <span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]<span class="hljs-keyword">int</span>)</span> <span class="hljs-params">(<span class="hljs-keyword">chan</span>&lt;- Request, &lt;-<span class="hljs-keyword">chan</span> Purchase)</span></span> {
    <span class="hljs-comment">// ...</span>
}
</code></pre>
<p>Buyer goroutines will send requests to the processor's input channel and receive results (successful or failed purchases) from the output channel:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">const</span> buyer = <span class="hljs-string">"Alice"</span>
    acc := <span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]<span class="hljs-keyword">int</span>{buyer: <span class="hljs-number">50</span>}

    wishlist := []LegoSet{
        {name: <span class="hljs-string">"Castle"</span>, price: <span class="hljs-number">40</span>},
        {name: <span class="hljs-string">"Plants"</span>, price: <span class="hljs-number">20</span>},
    }

    reqs, purs := Processor(acc)

    <span class="hljs-comment">// Alice buys stuff.</span>
    <span class="hljs-keyword">var</span> wg sync.WaitGroup
    <span class="hljs-keyword">for</span> _, set := <span class="hljs-keyword">range</span> wishlist {
        wg.Go(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
            reqs &lt;- Request{buyer: buyer, set: set}
            pur := &lt;-purs
            <span class="hljs-keyword">if</span> pur.succeed {
                fmt.Printf(<span class="hljs-string">"%s bought the %s\n"</span>, pur.buyer, pur.set.name)
                fmt.Printf(<span class="hljs-string">"%s's balance: %d\n"</span>, buyer, pur.balance)
            }
        })
    }
    wg.Wait()
}
</code></pre>
<pre><code>Alice bought the Plants
Alice<span class="hljs-string">'s balance: 30</span>
</code></pre><p>This approach offers several benefits:</p>
<ul>
<li>Buyer goroutines send their requests and get results without worrying about how the purchase is done.</li>
<li>All the buying logic is handled inside the processor goroutine.</li>
<li>No need for mutexes.</li>
</ul>
<p>All that's left is to implement the processor. How about this:</p>
<pre><code class="lang-go"><span class="hljs-comment">// Processor handles purchases.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">Processor</span><span class="hljs-params">(acc <span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]<span class="hljs-keyword">int</span>)</span> <span class="hljs-params">(<span class="hljs-keyword">chan</span>&lt;- Request, &lt;-<span class="hljs-keyword">chan</span> Purchase)</span></span> {
    in := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> Request)
    out := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> Purchase)
    acc = maps.Clone(acc)

    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">for</span> {
            <span class="hljs-comment">// Receive the purchase request.</span>
            req := &lt;-in

            <span class="hljs-comment">// Handle the purchase.</span>
            balance := acc[req.buyer]
            pur := Purchase{buyer: req.buyer, set: req.set, balance: balance}
            <span class="hljs-keyword">if</span> balance &gt;= req.set.price {
                pur.balance -= req.set.price
                pur.succeed = <span class="hljs-literal">true</span>
                acc[req.buyer] = pur.balance
            } <span class="hljs-keyword">else</span> {
                pur.succeed = <span class="hljs-literal">false</span>
            }

            <span class="hljs-comment">// Send the result.</span>
            out &lt;- pur
        }
    }()

    <span class="hljs-keyword">return</span> in, out
}
</code></pre>
<blockquote>
<p>It would have been a good idea to add a way to stop the processor using context, but I decided not to do it to keep the code simple.</p>
</blockquote>
<p>The processor clones the original account states and works with its own copy. This approach makes sure there is no concurrent access to the accounts, so there are no races. Of course, we should avoid running two processors at the same time, or we could end up with two different versions of the truth.</p>
<p>It's not always easy to structure a program in a way that avoids shared state. But if you can, it's a good option.</p>
<hr />
<h2 id="heading-summary">Summary</h2>
<p>Now you know how to protect shared data (from data races) and sequences of operations (from race conditions) in a concurrent environment using mutexes. Be careful with them and always test your code thoroughly with the race detector enabled.</p>
<p>Use code reviews, because the race detector doesn't catch every data race and can't detect race conditions at all. Having someone else look over your code can be really helpful.</p>
<p>Key points to remember:</p>
<ul>
<li><strong>Data races</strong> occur when multiple goroutines access shared data concurrently, with at least one modifying it.</li>
<li><strong>Race conditions</strong> occur when an unpredictable order of operations leads to incorrect system state.</li>
<li><strong>Atomic operations</strong> ensure that check-and-update sequences happen as a single unit.</li>
<li><strong>Compare-and-set</strong> patterns help make operations atomic.</li>
<li><strong>Shared nothing architecture</strong> avoids concurrency issues by eliminating shared state.</li>
<li><strong>TryLock</strong> can be useful but is rarely needed and may indicate design problems.</li>
</ul>
<p>Safe concurrent programming requires careful design and thorough testing.</p>
]]></content:encoded></item><item><title><![CDATA[Go: Managing Time and Timing in Concurrent Applications]]></title><description><![CDATA[In this chapter, we'll explore various techniques for managing time in concurrent Go programs.

Throttling
Suppose we have work that needs to be done in large quantities:
func work() {
    // Something very important, but not very fast.
    time.Slee...]]></description><link>https://blog.fshtab.com/go-time-handling-concurrent-programs</link><guid isPermaLink="true">https://blog.fshtab.com/go-time-handling-concurrent-programs</guid><dc:creator><![CDATA[Fedor S]]></dc:creator><pubDate>Fri, 22 Sep 2023 13:55:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1766406512359/338ec724-5861-4c59-945d-fad213c2d709.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this chapter, we'll explore various techniques for managing time in concurrent Go programs.</p>
<hr />
<h2 id="heading-throttling">Throttling</h2>
<p>Suppose we have work that needs to be done in large quantities:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">work</span><span class="hljs-params">()</span></span> {
    <span class="hljs-comment">// Something very important, but not very fast.</span>
    time.Sleep(<span class="hljs-number">100</span> * time.Millisecond)
}
</code></pre>
<p>The simplest approach is to process tasks sequentially:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    start := time.Now()

    work()
    work()
    work()
    work()

    fmt.Println(<span class="hljs-string">"4 calls took"</span>, time.Since(start))
}
</code></pre>
<pre><code><span class="hljs-number">4</span> calls took <span class="hljs-number">400</span>ms
</code></pre><p>Four calls of 100 ms each take a total of 400 ms when executed one after the other.</p>
<p>Of course, it's faster to do the work in parallel with N handlers like this:</p>
<ul>
<li>If there's a free handler, give it the task.</li>
<li>Otherwise, wait until one becomes available.</li>
</ul>
<p>In the "Channels" chapter we solved a similar problem using a semaphore. Recall the principle:</p>
<ul>
<li>Create an empty channel with a buffer size of N.</li>
<li>Before starting, a goroutine puts a token (some value) into the channel.</li>
<li>Once finished, the goroutine takes a token from the channel.</li>
</ul>
<p>Let's create a wrapper <code>throttle(n, fn)</code> to ensure concurrent execution. We'll set up a <code>sema</code> channel and make sure that no more than <code>n</code> work functions are running at the same time:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">throttle</span><span class="hljs-params">(n <span class="hljs-keyword">int</span>, fn <span class="hljs-keyword">func</span>()</span>) <span class="hljs-params">(handle <span class="hljs-keyword">func</span>()</span>, <span class="hljs-title">wait</span> <span class="hljs-title">func</span><span class="hljs-params">()</span>)</span> {
    <span class="hljs-comment">// Semaphore for n goroutines.</span>
    sema := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">struct</span>{}, n)

    <span class="hljs-comment">// Execute fn functions concurrently, but not more than n at a time.</span>
    handle = <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        sema &lt;- <span class="hljs-keyword">struct</span>{}{}
        <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
            fn()
            &lt;-sema
        }()
    }

    <span class="hljs-comment">// Wait until all functions have finished.</span>
    wait = <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">for</span> <span class="hljs-keyword">range</span> n {
            sema &lt;- <span class="hljs-keyword">struct</span>{}{}
        }
    }

    <span class="hljs-keyword">return</span> handle, wait
}
</code></pre>
<p>Now the client calls the <code>work()</code> function through the wrapper, not directly:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    handle, wait := throttle(<span class="hljs-number">2</span>, work)
    start := time.Now()

    handle()
    handle()
    handle()
    handle()
    wait()

    fmt.Println(<span class="hljs-string">"4 calls took"</span>, time.Since(start))
}
</code></pre>
<pre><code><span class="hljs-number">4</span> calls took <span class="hljs-number">200</span>ms
</code></pre><p>Here's how it works:</p>
<ul>
<li>The first and second calls start processing immediately;</li>
<li>The third and fourth wait for the previous two to finish.</li>
</ul>
<p>With two handlers, 4 calls complete in 200 ms.</p>
<p>Such throttling works well when the parallelism level <code>n</code> and the individual <code>work()</code> times match (more or less) the rate of <code>handle()</code> calls. Then each call has a good chance of being processed immediately or with a small delay.</p>
<p>However, if there are many more calls than the handlers can manage, the system will slow down. Each <code>work()</code> will still take 100 ms, but <code>handle()</code> calls will hang, waiting for a place in a semaphore. This isn't a big deal for data pipelines, but could be problematic for online requests.</p>
<p>Sometimes, clients may prefer to get an immediate error when all handlers are busy. We need another approach for such cases.</p>
<hr />
<h2 id="heading-backpressure">Backpressure</h2>
<p>Let's change the <code>throttle()</code> logic:</p>
<ul>
<li>If there's room in the semaphore, execute the function.</li>
<li>Otherwise, return an error immediately.</li>
</ul>
<p>This way, the client doesn't have to wait for a stuck call.</p>
<p>The select statement will help us once again.</p>
<p>Before:</p>
<pre><code class="lang-go"><span class="hljs-comment">// Execute fn functions concurrently,</span>
<span class="hljs-comment">// but not more than n at a time.</span>
handle = <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
    sema &lt;- <span class="hljs-keyword">struct</span>{}{}
    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        fn()
        &lt;-sema
    }()
}
</code></pre>
<p>After:</p>
<pre><code class="lang-go"><span class="hljs-comment">// Execute fn functions concurrently,</span>
<span class="hljs-comment">// but not more than n at a time.</span>
handle = <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span> <span class="hljs-title">error</span></span> {
    <span class="hljs-keyword">select</span> {
    <span class="hljs-keyword">case</span> sema &lt;- <span class="hljs-keyword">struct</span>{}{}:
        <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
            fn()
            &lt;-sema
        }()
        <span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
    <span class="hljs-keyword">default</span>:
        <span class="hljs-keyword">return</span> errors.New(<span class="hljs-string">"busy"</span>)
    }
}
</code></pre>
<p>Let's recall how select works:</p>
<ul>
<li>Checks which cases are not blocked.</li>
<li>If multiple cases are ready, randomly selects one to execute.</li>
<li>If all cases are blocked, waits until one is ready.</li>
</ul>
<p>The third point (all cases are blocked) actually splits into two:</p>
<ul>
<li>If there's <em>no default</em> case, select waits until one is ready.</li>
<li>If there is a <em>default</em> case, select executes it.</li>
</ul>
<p>The default case is perfect for our situation:</p>
<ul>
<li>If there's a token in the <code>sema</code> channel, we run <code>fn</code>.</li>
<li>Otherwise, we return a "busy" error without waiting.</li>
</ul>
<p>Let's look at the client:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    handle, wait := throttle(<span class="hljs-number">2</span>, work)

    start := time.Now()

    err := handle()
    fmt.Println(<span class="hljs-string">"1st call, error:"</span>, err)

    err = handle()
    fmt.Println(<span class="hljs-string">"2nd call, error:"</span>, err)

    err = handle()
    fmt.Println(<span class="hljs-string">"3rd call, error:"</span>, err)

    err = handle()
    fmt.Println(<span class="hljs-string">"4th call, error:"</span>, err)

    wait()

    fmt.Println(<span class="hljs-string">"4 calls took"</span>, time.Since(start))
}
</code></pre>
<pre><code><span class="hljs-number">1</span>st call, <span class="hljs-attr">error</span>: &lt;nil&gt;
2nd call, error: &lt;nil&gt;
3rd call, error: busy
4th call, error: busy
4 calls took 100ms
</code></pre><p>The first two calls ran concurrently (each took 100 ms), while the third and fourth got an error immediately. All calls were handled in 100 ms.</p>
<p>Of course, this approach (sometimes called <em>backpressure</em>) requires some awareness on the part of the client. The client should understand that a "busy" error means overload, and either delay further <code>handle()</code> calls or reduce their frequency.</p>
<hr />
<h2 id="heading-operation-timeout">Operation Timeout</h2>
<p>Here's a function that normally takes 10 ms, but in 20% of the calls it takes 200 ms:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">work</span><span class="hljs-params">()</span> <span class="hljs-title">int</span></span> {
    <span class="hljs-keyword">if</span> rand.Intn(<span class="hljs-number">10</span>) &lt; <span class="hljs-number">8</span> {
        time.Sleep(<span class="hljs-number">10</span> * time.Millisecond)
    } <span class="hljs-keyword">else</span> {
        time.Sleep(<span class="hljs-number">200</span> * time.Millisecond)
    }
    <span class="hljs-keyword">return</span> <span class="hljs-number">42</span>
}
</code></pre>
<p>Let's say we don't want to wait more than 50 ms. So, we set a <em>timeout</em> — the maximum time we're willing to wait for a response. If the operation doesn't complete within the timeout, we'll consider it an error.</p>
<p>Let's create a wrapper that runs the given function with the given timeout:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">withTimeout</span><span class="hljs-params">(timeout time.Duration, fn <span class="hljs-keyword">func</span>()</span> <span class="hljs-title">int</span>) <span class="hljs-params">(<span class="hljs-keyword">int</span>, error)</span></span> {
    <span class="hljs-comment">// ...</span>
}
</code></pre>
<p>We'll call it like this:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">for</span> <span class="hljs-keyword">range</span> <span class="hljs-number">10</span> {
        start := time.Now()
        timeout := <span class="hljs-number">50</span> * time.Millisecond
        <span class="hljs-keyword">if</span> answer, err := withTimeout(timeout, work); err != <span class="hljs-literal">nil</span> {
            fmt.Printf(<span class="hljs-string">"Took longer than %v. Error: %v\n"</span>, time.Since(start), err)
        } <span class="hljs-keyword">else</span> {
            fmt.Printf(<span class="hljs-string">"Took %v. Result: %v\n"</span>, time.Since(start), answer)
        }
    }
}
</code></pre>
<pre><code>Took <span class="hljs-number">10</span>ms. Result: <span class="hljs-number">42</span>
Took <span class="hljs-number">10</span>ms. Result: <span class="hljs-number">42</span>
Took <span class="hljs-number">10</span>ms. Result: <span class="hljs-number">42</span>
Took longer than <span class="hljs-number">50</span>ms. Error: timeout
Took <span class="hljs-number">10</span>ms. Result: <span class="hljs-number">42</span>
Took longer than <span class="hljs-number">50</span>ms. Error: timeout
Took <span class="hljs-number">10</span>ms. Result: <span class="hljs-number">42</span>
Took <span class="hljs-number">10</span>ms. Result: <span class="hljs-number">42</span>
Took <span class="hljs-number">10</span>ms. Result: <span class="hljs-number">42</span>
Took <span class="hljs-number">10</span>ms. Result: <span class="hljs-number">42</span>
</code></pre><p>Here's the idea behind <code>withTimeout()</code>:</p>
<ul>
<li>Run the given <code>fn()</code> in a separate goroutine.</li>
<li>Wait for the <code>timeout</code> period.</li>
<li>If <code>fn()</code> returns a result, return it.</li>
<li>If it doesn't finish in time, return an error.</li>
</ul>
<p>Here's how you can implement it:</p>
<pre><code class="lang-go"><span class="hljs-comment">// withTimeout executes a function with a given timeout.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">withTimeout</span><span class="hljs-params">(timeout time.Duration, fn <span class="hljs-keyword">func</span>()</span> <span class="hljs-title">int</span>) <span class="hljs-params">(<span class="hljs-keyword">int</span>, error)</span></span> {
    <span class="hljs-keyword">var</span> result <span class="hljs-keyword">int</span>

    done := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">struct</span>{})
    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        result = fn()
        <span class="hljs-built_in">close</span>(done)
    }()

    <span class="hljs-keyword">select</span> {
    <span class="hljs-keyword">case</span> &lt;-done:
        <span class="hljs-keyword">return</span> result, <span class="hljs-literal">nil</span>
    <span class="hljs-keyword">case</span> &lt;-time.After(timeout):
        <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>, errors.New(<span class="hljs-string">"timeout"</span>)
    }
}
</code></pre>
<p>The <code>time.After(d)</code> function returns a channel that will receive a value after duration <code>d</code>. It's a convenient way to implement timeouts.</p>
<hr />
<h2 id="heading-timer">Timer</h2>
<p>A timer is a mechanism for executing code after a certain delay. Go provides the <code>time.Timer</code> type for this purpose.</p>
<h3 id="heading-basic-timer-usage">Basic Timer Usage</h3>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    timer := time.NewTimer(<span class="hljs-number">2</span> * time.Second)
    &lt;-timer.C
    fmt.Println(<span class="hljs-string">"Timer fired!"</span>)
}
</code></pre>
<p><code>NewTimer(d)</code> creates a timer that will send the current time to channel <code>C</code> after duration <code>d</code>. You can stop the timer before it fires using <code>Stop()</code>:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    timer := time.NewTimer(<span class="hljs-number">2</span> * time.Second)
    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        time.Sleep(<span class="hljs-number">1</span> * time.Second)
        <span class="hljs-keyword">if</span> timer.Stop() {
            fmt.Println(<span class="hljs-string">"Timer stopped"</span>)
        }
    }()
    &lt;-timer.C
    fmt.Println(<span class="hljs-string">"Timer fired!"</span>)
}
</code></pre>
<h3 id="heading-timer-reset">Timer Reset</h3>
<p>Sometimes you need to reset a timer to extend or restart its duration. However, there are important considerations when resetting timers.</p>
<p>Let's consider a consumer that processes tokens and logs a warning if no tokens arrive within a timeout period:</p>
<pre><code class="lang-go"><span class="hljs-keyword">type</span> token <span class="hljs-keyword">struct</span>{}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">consumer</span><span class="hljs-params">(cancel &lt;-<span class="hljs-keyword">chan</span> token, in &lt;-<span class="hljs-keyword">chan</span> token)</span></span> {
    <span class="hljs-keyword">const</span> timeout = time.Hour
    <span class="hljs-keyword">for</span> {
        <span class="hljs-keyword">select</span> {
        <span class="hljs-keyword">case</span> &lt;-in:
            <span class="hljs-comment">// do stuff</span>
        <span class="hljs-keyword">case</span> &lt;-time.After(timeout):
            <span class="hljs-comment">// log warning</span>
        <span class="hljs-keyword">case</span> &lt;-cancel:
            <span class="hljs-keyword">return</span>
        }
    }
}
</code></pre>
<p>Let's write a client that measures the memory usage after 100K channel sends:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    cancel := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> token)
    <span class="hljs-keyword">defer</span> <span class="hljs-built_in">close</span>(cancel)

    tokens := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> token)
    <span class="hljs-keyword">go</span> consumer(cancel, tokens)

    measure(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">for</span> <span class="hljs-keyword">range</span> <span class="hljs-number">100000</span> {
            tokens &lt;- token{}
        }
    })
}
</code></pre>
<pre><code>Memory used: <span class="hljs-number">24223</span> KB, # allocations: <span class="hljs-number">300011</span>
</code></pre><p>Behind the scenes, each <code>time.After</code> creates a timer that is later freed by the garbage collector. So our for loop is essentially creating a myriad of timers, doing a lot of allocations, and creating unnecessary work for the GC. This is usually not what we want.</p>
<p>To avoid creating a timer on each loop iteration, you can create it at the beginning and reset it before moving on to the next iteration. The <code>Reset</code> method in Go 1.23+ is perfect for this:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">consumer</span><span class="hljs-params">(cancel &lt;-<span class="hljs-keyword">chan</span> token, in &lt;-<span class="hljs-keyword">chan</span> token)</span></span> {
    <span class="hljs-keyword">const</span> timeout = time.Hour
    timer := time.NewTimer(timeout)
    <span class="hljs-keyword">for</span> {
        timer.Reset(timeout)
        <span class="hljs-keyword">select</span> {
        <span class="hljs-keyword">case</span> &lt;-in:
            <span class="hljs-comment">// do stuff</span>
        <span class="hljs-keyword">case</span> &lt;-timer.C:
            <span class="hljs-comment">// log warning</span>
        <span class="hljs-keyword">case</span> &lt;-cancel:
            <span class="hljs-keyword">return</span>
        }
    }
}
</code></pre>
<pre><code>Memory used: <span class="hljs-number">0</span> KB, # allocations: <span class="hljs-number">2</span>
</code></pre><p>This approach does not create new timers, so the GC does not need to collect them.</p>
<h3 id="heading-reset-in-go-pre-123">Reset in Go pre-1.23</h3>
<p>Due to implementation quirks in Go versions prior to 1.23, <code>Reset</code> should only be called on an already stopped or expired timer with an empty output channel. So, to reset the timer correctly, you have to use a helper function:</p>
<pre><code class="lang-go"><span class="hljs-comment">// resetTimer stops, drains and resets the timer.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">resetTimer</span><span class="hljs-params">(t *time.Timer, d time.Duration)</span></span> {
    <span class="hljs-keyword">if</span> !t.Stop() {
        <span class="hljs-keyword">select</span> {
        <span class="hljs-keyword">case</span> &lt;-t.C:
        <span class="hljs-keyword">default</span>:
        }
    }
    t.Reset(d)
}
</code></pre>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">consumer</span><span class="hljs-params">(cancel &lt;-<span class="hljs-keyword">chan</span> token, in &lt;-<span class="hljs-keyword">chan</span> token)</span></span> {
    <span class="hljs-keyword">const</span> timeout = time.Hour
    timer := time.NewTimer(timeout)
    <span class="hljs-keyword">for</span> {
        resetTimer(timer, timeout)
        <span class="hljs-keyword">select</span> {
        <span class="hljs-keyword">case</span> &lt;-in:
            <span class="hljs-comment">// do stuff</span>
        <span class="hljs-keyword">case</span> &lt;-timer.C:
            <span class="hljs-comment">// log warning</span>
        <span class="hljs-keyword">case</span> &lt;-cancel:
            <span class="hljs-keyword">return</span>
        }
    }
}
</code></pre>
<pre><code>Memory used: <span class="hljs-number">0</span> KB, # allocations: <span class="hljs-number">2</span>
</code></pre><h3 id="heading-timeafterfunc">time.AfterFunc</h3>
<p>To make matters worse, <code>time.AfterFunc</code> also creates a timer, but a very different one. It has a nil <code>C</code> channel, so the <code>Reset</code> method works differently:</p>
<ul>
<li>If the timer is still active (not stopped, not expired), <code>Reset</code> clears the timeout, effectively restarting the timer.</li>
<li>If the timer is already stopped or expired, <code>Reset</code> schedules a new function execution.</li>
</ul>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    <span class="hljs-keyword">var</span> start time.Time

    work := <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        fmt.Printf(<span class="hljs-string">"work done after %dms\n"</span>, time.Since(start).Milliseconds())
    }

    <span class="hljs-comment">// run work after 10 milliseconds</span>
    timeout := <span class="hljs-number">10</span> * time.Millisecond
    start = time.Now()  <span class="hljs-comment">// ignore the data race for simplicity</span>
    t := time.AfterFunc(timeout, work)

    <span class="hljs-comment">// wait for 5 to 15 milliseconds</span>
    delay := time.Duration(<span class="hljs-number">5</span>+rand.Intn(<span class="hljs-number">11</span>)) * time.Millisecond
    time.Sleep(delay)
    fmt.Printf(<span class="hljs-string">"%dms has passed...\n"</span>, delay.Milliseconds())

    <span class="hljs-comment">// Reset behavior depends on whether the timer has expired</span>
    t.Reset(timeout)
    start = time.Now()

    time.Sleep(<span class="hljs-number">50</span>*time.Millisecond)
}
</code></pre>
<p>If the timer has not expired, <code>Reset</code> clears the timeout:</p>
<pre><code><span class="hljs-number">8</span>ms has passed...
work done after <span class="hljs-number">10</span>ms
</code></pre><p>If the timer has expired, <code>Reset</code> schedules a new function call:</p>
<pre><code>work done after <span class="hljs-number">10</span>ms
<span class="hljs-number">13</span>ms has passed...
work done after <span class="hljs-number">10</span>ms
</code></pre><p>To reiterate:</p>
<ul>
<li>Go ≤ 1.22: For a <code>Timer</code> created with <code>NewTimer</code>, <code>Reset</code> should only be called on stopped or expired timers with drained channels.</li>
<li>Go ≥ 1.23: For a <code>Timer</code> created with <code>NewTimer</code>, it's safe to call <code>Reset</code> on timers in any state (active, stopped or expired). No channel drain is required.</li>
<li>For a <code>Timer</code> created with <code>AfterFunc</code>, <code>Reset</code> either reschedules the function (if the timer is still active) or schedules the function to run again (if the timer has stopped or expired).</li>
</ul>
<p>Timers are not the most obvious things in Go, are they?</p>
<hr />
<h2 id="heading-ticker">Ticker</h2>
<p>Sometimes you want to perform an action at regular intervals. There's a tool for this in Go called a <em>ticker</em>. A ticker is like a timer, but it keeps firing until you stop it:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">work</span><span class="hljs-params">(at time.Time)</span></span> {
    fmt.Printf(<span class="hljs-string">"%s: work done\n"</span>, at.Format(<span class="hljs-string">"15:04:05.000"</span>))
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    ticker := time.NewTicker(<span class="hljs-number">50</span> * time.Millisecond)
    <span class="hljs-keyword">defer</span> ticker.Stop()

    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">for</span> {
            at := &lt;-ticker.C
            work(at)
        }
    }()

    <span class="hljs-comment">// enough for 5 ticks</span>
    time.Sleep(<span class="hljs-number">260</span> * time.Millisecond)
}
</code></pre>
<pre><code><span class="hljs-number">07</span>:<span class="hljs-number">20</span>:<span class="hljs-number">00.150</span>: work done
<span class="hljs-number">07</span>:<span class="hljs-number">20</span>:<span class="hljs-number">00.200</span>: work done
<span class="hljs-number">07</span>:<span class="hljs-number">20</span>:<span class="hljs-number">00.250</span>: work done
<span class="hljs-number">07</span>:<span class="hljs-number">20</span>:<span class="hljs-number">00.300</span>: work done
<span class="hljs-number">07</span>:<span class="hljs-number">20</span>:<span class="hljs-number">00.350</span>: work done
</code></pre><p><code>NewTicker(d)</code> creates a ticker that sends the current time to the channel <code>C</code> at interval <code>d</code>. You must stop the ticker eventually with <code>Stop()</code> to free up resources.</p>
<p>In our case, the interval is 50 ms, which allows for 5 ticks.</p>
<p>If the channel reader can't keep up with the ticker, the ticker will skip ticks:</p>
<pre><code class="lang-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">work</span><span class="hljs-params">(at time.Time)</span></span> {
    fmt.Printf(<span class="hljs-string">"%s: work done\n"</span>, at.Format(<span class="hljs-string">"15:04:05.000"</span>))
    time.Sleep(<span class="hljs-number">100</span> * time.Millisecond)
}

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    ticker := time.NewTicker(<span class="hljs-number">50</span> * time.Millisecond)
    <span class="hljs-keyword">defer</span> ticker.Stop()

    <span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
        <span class="hljs-keyword">for</span> {
            at := &lt;-ticker.C
            work(at)
        }
    }()

    <span class="hljs-comment">// enough for 3 ticks because of the slow work()</span>
    time.Sleep(<span class="hljs-number">260</span> * time.Millisecond)
}
</code></pre>
<pre><code><span class="hljs-number">07</span>:<span class="hljs-number">20</span>:<span class="hljs-number">00.150</span>: work done
<span class="hljs-number">07</span>:<span class="hljs-number">20</span>:<span class="hljs-number">00.200</span>: work done
<span class="hljs-number">07</span>:<span class="hljs-number">20</span>:<span class="hljs-number">00.300</span>: work done
</code></pre><p>In this case, the receiver starts to fall behind after the second tick.</p>
<p>As you can see, the ticks don't pile up; they adapt to the slow receiver.</p>
<hr />
<h2 id="heading-summary">Summary</h2>
<p>Now you know that handling time in concurrent programs is not about (ab)using <code>time.Sleep</code>. Here are some useful tools you've learned:</p>
<ul>
<li><strong>Timeouts</strong> limit operation time.</li>
<li><strong>Timers</strong> help with delayed operations.</li>
<li><strong>Tickers</strong> are for periodic actions.</li>
<li><strong>Default case in select</strong> allows non-blocking processing.</li>
</ul>
<p>These tools provide efficient and reliable ways to manage time-based operations in concurrent Go programs.</p>
]]></content:encoded></item></channel></rss>