<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Concurrency on WebDevStation</title>
    <link>https://webdevstation.com/tags/concurrency/</link>
    <description>7 articles tagged Concurrency — tutorials, code examples and notes from building real systems, newest first.</description>
    <generator>Hugo</generator>
    <language>en</language>
    <managingEditor>Alex</managingEditor>
    <webMaster>Alex</webMaster>
    <copyright>© 2026 WebDevStation</copyright>
    <lastBuildDate>Tue, 01 Sep 2026 18:30:00 +0200</lastBuildDate>
    <atom:link href="https://webdevstation.com/tags/concurrency/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>What&#39;s New in Go 1.27: Generic Methods, JSON v2 and uuid in the Standard Library</title>
      <link>https://webdevstation.com/posts/whats-new-in-go-1-27/</link>
      <pubDate>Tue, 01 Sep 2026 18:30:00 +0200</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/whats-new-in-go-1-27/</guid>
      <description>Go 1.27 finally adds generic methods to the language, promotes encoding/json/v2 into the standard library, ships a uuid package, and makes goroutine leak profiles…</description>
      <content:encoded><![CDATA[<p>Most Go releases are a handful of library additions and a compiler that got a bit faster. Go 1.27, out in August 2026, is not that release. It changes the language — generic methods are finally here — and it lands two things people have been waiting years for: a second-generation JSON package and a <code>uuid</code> package, both in the standard library.</p>
<h2 id="generic-methods">Generic Methods</h2>
<p>This is the headline, and it is a real language change: <strong>a method declaration may now declare its own type parameters.</strong></p>
<p>Before 1.27, type parameters could only appear on a function or on the type itself. If you wanted a generic helper that logically belonged to a type, your only option was a package-level function:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// Go 1.26 and earlier: the helper lives in the package namespace,</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// even though it only makes sense for *Rand.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">N</span>[<span style="color:#a6e22e">Int</span> <span style="color:#a6e22e">intType</span>](<span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">rand</span>.<span style="color:#a6e22e">Rand</span>, <span style="color:#a6e22e">n</span> <span style="color:#a6e22e">Int</span>) <span style="color:#a6e22e">Int</span>
</span></span></code></pre></div><p>Now it can live where it belongs. <code>math/rand/v2</code> is the standard library&rsquo;s own example — it gained a generic <em>method</em> alongside the existing generic function:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// Go 1.27</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">Rand</span>) <span style="color:#a6e22e">N</span>[<span style="color:#a6e22e">Int</span> <span style="color:#a6e22e">intType</span>](<span style="color:#a6e22e">n</span> <span style="color:#a6e22e">Int</span>) <span style="color:#a6e22e">Int</span>
</span></span></code></pre></div><p>Which means this now compiles:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">r</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">rand</span>.<span style="color:#a6e22e">New</span>(<span style="color:#a6e22e">rand</span>.<span style="color:#a6e22e">NewPCG</span>(<span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">2</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">d</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Duration</span> = <span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">N</span>(<span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>)  <span style="color:#75715e">// method, inferred as time.Duration</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">i</span> <span style="color:#66d9ef">int32</span>         = <span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">N</span>(int32(<span style="color:#ae81ff">100</span>))   <span style="color:#75715e">// same method, different instantiation</span>
</span></span></code></pre></div><p>The practical effect is that you can stop writing <code>pkg.DoThing(x, ...)</code> for operations that are conceptually <code>x.DoThing(...)</code>. Anywhere you have a container, a client, or a builder that needed a package-level generic helper, that helper can now be a method.</p>
<p>Two limits worth knowing before you plan a refactor around it:</p>
<ul>
<li><strong>Interfaces cannot declare type parameters on their methods</strong>, and an interface method cannot be satisfied by a generic method. So this does not give you generic interfaces through the back door; the method set of an interface is still fully concrete.</li>
<li>It is a <em>method</em> type parameter, distinct from the receiver&rsquo;s. <code>func (c *Cache[K, V]) MapTo[T any](...)</code> has three type parameters in play, two from the receiver and one from the method.</li>
</ul>
<p>If you have been writing the awkward package-level workarounds I described in <a href="/posts/example-of-how-generics-simplify-golang/">how Golang generics minimize the amount of code you need to write</a>, this is the release where a lot of that gets to move inside the type.</p>
<p>Two smaller language changes shipped alongside it. A key in a struct literal may now be <strong>any valid field selector</strong>, not just a top-level field name — which makes literals for structs with embedded types far less irritating. And function type inference is <strong>generalised to every context</strong> where a generic function is assigned to, or converted to, a matching function type, so a class of &ldquo;cannot infer&rdquo; errors simply disappears.</p>
<h2 id="encodingjsonv2">encoding/json/v2</h2>
<p>The JSON rewrite is in, as two packages:</p>
<ul>
<li><strong><code>encoding/json/v2</code></strong> — the high-level API you already know, redesigned.</li>
<li><strong><code>encoding/json/jsontext</code></strong> — lower-level syntactic processing, with an <code>Encoder</code> and <code>Decoder</code> that work in terms of <code>Token</code> and <code>Value</code> and hold a state machine guaranteeing the JSON is valid.</li>
</ul>
<p>The v2 entry points take variadic options:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">import</span> <span style="color:#e6db74">&#34;encoding/json/v2&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">b</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">json</span>.<span style="color:#a6e22e">Marshal</span>(<span style="color:#a6e22e">v</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">json</span>.<span style="color:#a6e22e">Deterministic</span>(<span style="color:#66d9ef">true</span>),          <span style="color:#75715e">// stable map ordering</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">json</span>.<span style="color:#a6e22e">OmitZeroStructFields</span>(<span style="color:#66d9ef">true</span>),
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">err</span> = <span style="color:#a6e22e">json</span>.<span style="color:#a6e22e">Unmarshal</span>(<span style="color:#a6e22e">data</span>, <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">v</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">json</span>.<span style="color:#a6e22e">RejectUnknownMembers</span>(<span style="color:#66d9ef">true</span>),   <span style="color:#75715e">// strict decoding, no silent drops</span>
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>Alongside <code>Marshal</code> and <code>Unmarshal</code> there are <code>MarshalWrite</code> / <code>UnmarshalRead</code> for an <code>io.Writer</code> / <code>io.Reader</code>, and <code>MarshalEncode</code> / <code>UnmarshalDecode</code> for driving a <code>jsontext.Encoder</code> / <code>Decoder</code> directly. That last pair is what you want for streaming, and it is a much better story than v1&rsquo;s <code>json.Decoder</code> ever was.</p>
<p><strong>The defaults are stricter</strong>, which is the whole point. v2 rejects invalid UTF-8 in strings and rejects duplicate names within an object — both of which v1 quietly accepted. That is more interoperable and it closes a genuine class of parser-differential bugs.</p>
<p><code>RejectUnknownMembers(true)</code> deserves a callout for anyone writing an API. v1&rsquo;s default of silently discarding unknown fields is how a typo in a client&rsquo;s request body turns into a field that is quietly zero, and then into a support ticket. Making that an error at the boundary is the same instinct as the sentinel errors in <a href="/posts/error-handling-in-go/">error handling in Go</a>: fail where the information is, not three layers down.</p>
<p><code>encoding/json</code> v1 is not going anywhere, and it gained a matching set of options with names that tell you exactly what they are for — <code>CallMethodsWithLegacySemantics</code>, <code>OmitEmptyWithLegacySemantics</code>, <code>FormatDurationAsNano</code>, <code>ParseTimeWithLooseRFC3339</code> and friends. That is the migration path: move to v2, and where behaviour differs in a way you depend on, opt back into the v1 semantics explicitly rather than discovering the difference in production.</p>
<p>One thing to be deliberate about: <strong>v2&rsquo;s stricter parsing is a behaviour change at your HTTP boundary.</strong> A client that has been sending duplicate keys or malformed UTF-8 and getting away with it will start getting errors. That is correct, and it is still worth knowing before you flip it on a live service. Point a load test at it first — the setup in <a href="/posts/an-easy-way-to-loadtest-your-web-apps/">an easy way to load test your web apps</a> is enough to see whether real traffic trips the new rules.</p>
<h2 id="a-uuid-package">A uuid Package</h2>
<p>There is now a <code>uuid</code> package in the standard library. The whole API is small enough to quote:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">UUID</span> [<span style="color:#ae81ff">16</span>]<span style="color:#66d9ef">byte</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">New</span>() <span style="color:#a6e22e">UUID</span>                        <span style="color:#75715e">// the recommended default</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">NewV4</span>() <span style="color:#a6e22e">UUID</span>                      <span style="color:#75715e">// random</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">NewV7</span>() <span style="color:#a6e22e">UUID</span>                      <span style="color:#75715e">// time-ordered</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">Parse</span>(<span style="color:#66d9ef">string</span>) (<span style="color:#a6e22e">UUID</span>, <span style="color:#66d9ef">error</span>)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">MustParse</span>(<span style="color:#66d9ef">string</span>) <span style="color:#a6e22e">UUID</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">Nil</span>() <span style="color:#a6e22e">UUID</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">Max</span>() <span style="color:#a6e22e">UUID</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">UUID</span>) <span style="color:#a6e22e">String</span>() <span style="color:#66d9ef">string</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">UUID</span>) <span style="color:#a6e22e">Compare</span>(<span style="color:#a6e22e">UUID</span>) <span style="color:#66d9ef">int</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">UUID</span>) <span style="color:#a6e22e">MarshalText</span>() ([]<span style="color:#66d9ef">byte</span>, <span style="color:#66d9ef">error</span>)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">UUID</span>) <span style="color:#a6e22e">AppendText</span>([]<span style="color:#66d9ef">byte</span>) ([]<span style="color:#66d9ef">byte</span>, <span style="color:#66d9ef">error</span>)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#f92672">*</span><span style="color:#a6e22e">UUID</span>) <span style="color:#a6e22e">UnmarshalText</span>([]<span style="color:#66d9ef">byte</span>) <span style="color:#66d9ef">error</span>
</span></span></code></pre></div><p><code>NewV7</code> is the one to reach for when the UUID is a database key. Version 7 is time-ordered, so generated values sort roughly by creation time — which keeps B-tree index inserts near the right-hand edge instead of scattering them across the whole index the way v4 does. If you have ever watched write throughput degrade on a table with a random-UUID primary key, that is the problem v7 exists to solve.</p>
<p><code>Compare</code> returning an <code>int</code> means it drops straight into <code>slices.SortFunc</code>, and the <code>TextMarshaler</code> / <code>TextUnmarshaler</code> implementation means it round-trips through JSON and <code>database/sql</code> without a wrapper type.</p>
<p>The obvious question: does this kill <code>github.com/google/uuid</code>? Not immediately — plenty of code depends on its wider surface. But for the common case of &ldquo;I need a v4 or v7 UUID, and I need to parse one&rdquo;, that is now a dependency you can delete.</p>
<h2 id="goroutine-leak-profiles-generally-available">Goroutine Leak Profiles, Generally Available</h2>
<p>The <code>goroutineleak</code> profile, experimental in 1.26, is now GA in <code>runtime/pprof</code> and exposed at <code>/debug/pprof/goroutineleak</code>.</p>
<p>It reports goroutines blocked on a concurrency primitive that <strong>cannot possibly become unblocked</strong>. The detection is clever: it piggybacks on the garbage collector. If goroutine G is blocked on primitive P, and P is unreachable from any runnable goroutine — or from any goroutine those could unblock — then nothing can ever signal P, so G is leaked.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">import</span> <span style="color:#a6e22e">_</span> <span style="color:#e6db74">&#34;net/http/pprof&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// then:</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">//   go tool pprof http://localhost:6060/debug/pprof/goroutineleak</span>
</span></span></code></pre></div><p>The reachability trick is also the limitation, and the release notes say so plainly: leaks caused by blocking on a primitive that is still reachable through a global, or through a runnable goroutine&rsquo;s locals, will not be detected. It catches a large class, not all of them.</p>
<p>Still, this is the first tool that turns &ldquo;we think we have a goroutine leak somewhere&rdquo; into a list. The failure modes it finds are exactly the ones from <a href="/posts/worker-pools-in-go-with-errgroup/">worker pools in Go with errgroup</a> — a worker blocked forever on a send to a channel nobody is reading — and the ones a missing <code>&lt;-ctx.Done()</code> produces, which I wrote about in <a href="/posts/understanding-golang-context/">understanding Golang context</a>.</p>
<h2 id="free-performance">Free Performance</h2>
<p>The compiler now emits calls to <strong>size-specialised allocation routines</strong>, cutting the cost of small allocations (under 80 bytes) by up to 30%. The release notes are honest about what that means end to end: roughly <strong>1% on allocation-heavy programs</strong>, and about 60 KB more binary, regardless of workload.</p>
<p>One percent for a recompile is a good trade. If it causes you trouble, <code>GOEXPERIMENT=nosizespecializedmalloc</code> turns it off at build time — but note that escape hatch is expected to be <strong>removed in Go 1.28</strong>, so treat it as a window to file a bug, not a setting to keep.</p>
<h2 id="smaller-things-worth-knowing">Smaller Things Worth Knowing</h2>
<table>
	<thead>
			<tr>
					<th>Change</th>
					<th>Why you care</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>strings.CutLast(s, sep)</code></td>
					<td><code>Cut</code> from the right. The one you kept writing by hand with <code>LastIndex</code>.</td>
			</tr>
			<tr>
					<td><code>testing/synctest.Sleep(d)</code></td>
					<td>Sleep inside a synthetic-time bubble, without the awkward dance around the fake clock.</td>
			</tr>
			<tr>
					<td><code>hash/maphash.Hasher[T]</code> and <code>ComparableHasher[T]</code></td>
					<td>A real interface for hashing your own types — building a custom hash map stops meaning &ldquo;reimplement hashing&rdquo;.</td>
			</tr>
			<tr>
					<td><code>net/http.Server.MaxHeaderValueCount</code> (default 500)</td>
					<td>A cap on header value count, defending against header-flood requests.</td>
			</tr>
			<tr>
					<td><code>net/http.Server.DisableClientPriority</code></td>
					<td>Ignore client-supplied HTTP/2 priority hints.</td>
			</tr>
			<tr>
					<td><code>database/sql/driver.RowsColumnScanner</code></td>
					<td>Drivers can take over per-column scanning, which is how you avoid a round trip through <code>interface{}</code> for every value.</td>
			</tr>
			<tr>
					<td><code>go doc pkg@version</code></td>
					<td>Read the docs for a <em>specific</em> version without changing your module.</td>
			</tr>
			<tr>
					<td><code>go test</code> runs <code>stdversion</code> by default</td>
					<td>Catches use of stdlib symbols newer than the Go version your <code>go.mod</code> declares.</td>
			</tr>
	</tbody>
</table>
<p>That last one is quietly excellent. It is the failure mode where your code builds on your machine and breaks on a builder pinned to an older Go — the same class of problem I hit deploying this blog, where a template function that did not exist in the CI toolchain failed a build that was green locally.</p>
<h2 id="two-for-later">Two for Later</h2>
<p><strong><code>crypto/mldsa</code></strong> implements ML-DSA, the post-quantum signature scheme standardised as FIPS 204, with the three parameter sets <code>MLDSA44()</code>, <code>MLDSA65()</code> and <code>MLDSA87()</code>. Unless you have a compliance requirement you will not touch it this year, but it being in the standard library is what makes migration a normal task rather than a project.</p>
<p><strong><code>simd</code></strong> is an experimental package behind <code>GOEXPERIMENT=simd</code>, providing portable, vector-size-agnostic SIMD types like <code>Int8s</code> and <code>Float32s</code> that use hardware instructions where available. Experimental means experimental — but portable SIMD in Go is a genuinely interesting direction.</p>
<h2 id="should-you-upgrade">Should You Upgrade?</h2>
<p>Yes, and the calculus is unusually simple:</p>
<ul>
<li>The Go 1 compatibility promise holds; almost everything compiles unchanged.</li>
<li>You get ~1% on allocation-heavy code for a recompile.</li>
<li><code>goroutineleak</code> is worth the upgrade on its own for any service running long-lived goroutines.</li>
<li>Generic methods and JSON v2 are opt-in. Nothing forces you to rewrite anything.</li>
</ul>
<p>The only change that needs a moment&rsquo;s thought is JSON v2&rsquo;s stricter parsing, and only if you adopt it at a public boundary. Everything else is additive.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Generic methods close a gap that has been awkward since generics landed in 1.18, and they will quietly improve a lot of library APIs over the next year. JSON v2 is the rarer thing: a second attempt at a core package that gets to fix the defaults, with an explicit escape hatch back to the old behaviour. And a <code>uuid</code> package removes a dependency from nearly every service I have written.</p>
<p>Not bad for a release the notes describe as &ldquo;mostly implementation&rdquo;.</p>]]></content:encoded>
      <category>Go Programming</category>
      <category>Backend Development</category>
    </item>
    <item>
      <title>Tool Use in Go: Building an Agent Loop You Can Actually Debug</title>
      <link>https://webdevstation.com/posts/tool-use-in-go-agent-loop/</link>
      <pubDate>Sat, 29 Aug 2026 10:10:00 +0200</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/tool-use-in-go-agent-loop/</guid>
      <description>How LLM tool use really works in Go: the agentic loop, the SDK&#39;s tool runner, parallel tool calls, bounded concurrency, returning errors as tool results, and the…</description>
      <content:encoded><![CDATA[<p>&ldquo;Agent&rdquo; is doing a lot of work as a word right now. Strip the marketing off and what is underneath is a <code>for</code> loop: you send a message, the model asks you to run something, you run it, you send the result back, repeat until it stops asking. That is genuinely all it is — and once you have written the loop yourself, most of the mystique evaporates and what is left is a set of very ordinary Go problems.</p>
<h2 id="the-loop-in-full">The Loop, In Full</h2>
<p>Here is a complete manual loop. It is worth reading once even if you end up using the SDK&rsquo;s runner, because everything that goes wrong later is easier to diagnose when you know this shape. It assumes you already have a client and know how to read a response — if not, start with <a href="/posts/calling-claude-from-go/">calling an LLM from Go</a>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;context&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;encoding/json&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;fmt&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;log&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;github.com/anthropics/anthropic-sdk-go&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">client</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">NewClient</span>()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">addTool</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">ToolParam</span>{
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">Name</span>:        <span style="color:#e6db74">&#34;add&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">Description</span>: <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">String</span>(<span style="color:#e6db74">&#34;Add two integers&#34;</span>),
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">InputSchema</span>: <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">ToolInputSchemaParam</span>{
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">Properties</span>: <span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">string</span>]<span style="color:#66d9ef">any</span>{
</span></span><span style="display:flex;"><span>                <span style="color:#e6db74">&#34;a&#34;</span>: <span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">string</span>]<span style="color:#66d9ef">any</span>{<span style="color:#e6db74">&#34;type&#34;</span>: <span style="color:#e6db74">&#34;integer&#34;</span>},
</span></span><span style="display:flex;"><span>                <span style="color:#e6db74">&#34;b&#34;</span>: <span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">string</span>]<span style="color:#66d9ef">any</span>{<span style="color:#e6db74">&#34;type&#34;</span>: <span style="color:#e6db74">&#34;integer&#34;</span>},
</span></span><span style="display:flex;"><span>            },
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">tools</span> <span style="color:#f92672">:=</span> []<span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">ToolUnionParam</span>{{<span style="color:#a6e22e">OfTool</span>: <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">addTool</span>}}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">messages</span> <span style="color:#f92672">:=</span> []<span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">MessageParam</span>{
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">NewUserMessage</span>(<span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">NewTextBlock</span>(<span style="color:#e6db74">&#34;What is 2 + 3?&#34;</span>)),
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">resp</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">client</span>.<span style="color:#a6e22e">Messages</span>.<span style="color:#a6e22e">New</span>(<span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Background</span>(), <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">MessageNewParams</span>{
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">Model</span>:     <span style="color:#e6db74">&#34;claude-opus-5&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">MaxTokens</span>: <span style="color:#ae81ff">16000</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">Messages</span>:  <span style="color:#a6e22e">messages</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">Tools</span>:     <span style="color:#a6e22e">tools</span>,
</span></span><span style="display:flex;"><span>        })
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Fatal</span>(<span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// Append the assistant turn BEFORE handling the tool calls.</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">messages</span> = append(<span style="color:#a6e22e">messages</span>, <span style="color:#a6e22e">resp</span>.<span style="color:#a6e22e">ToParam</span>())
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">toolResults</span> []<span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">ContentBlockParamUnion</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">block</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">resp</span>.<span style="color:#a6e22e">Content</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">switch</span> <span style="color:#a6e22e">variant</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">block</span>.<span style="color:#a6e22e">AsAny</span>().(<span style="color:#66d9ef">type</span>) {
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">case</span> <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">TextBlock</span>:
</span></span><span style="display:flex;"><span>                <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#a6e22e">variant</span>.<span style="color:#a6e22e">Text</span>)
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">case</span> <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">ToolUseBlock</span>:
</span></span><span style="display:flex;"><span>                <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">in</span> <span style="color:#66d9ef">struct</span> {
</span></span><span style="display:flex;"><span>                    <span style="color:#a6e22e">A</span> <span style="color:#66d9ef">int</span> <span style="color:#e6db74">`json:&#34;a&#34;`</span>
</span></span><span style="display:flex;"><span>                    <span style="color:#a6e22e">B</span> <span style="color:#66d9ef">int</span> <span style="color:#e6db74">`json:&#34;b&#34;`</span>
</span></span><span style="display:flex;"><span>                }
</span></span><span style="display:flex;"><span>                <span style="color:#75715e">// block.Input is raw JSON — parse it, never string-match it.</span>
</span></span><span style="display:flex;"><span>                <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">json</span>.<span style="color:#a6e22e">Unmarshal</span>([]byte(<span style="color:#a6e22e">variant</span>.<span style="color:#a6e22e">JSON</span>.<span style="color:#a6e22e">Input</span>.<span style="color:#a6e22e">Raw</span>()), <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">in</span>); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>                    <span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Fatal</span>(<span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>                }
</span></span><span style="display:flex;"><span>                <span style="color:#a6e22e">result</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Sprintf</span>(<span style="color:#e6db74">&#34;%d&#34;</span>, <span style="color:#a6e22e">in</span>.<span style="color:#a6e22e">A</span><span style="color:#f92672">+</span><span style="color:#a6e22e">in</span>.<span style="color:#a6e22e">B</span>)
</span></span><span style="display:flex;"><span>                <span style="color:#a6e22e">toolResults</span> = append(<span style="color:#a6e22e">toolResults</span>,
</span></span><span style="display:flex;"><span>                    <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">NewToolResultBlock</span>(<span style="color:#a6e22e">block</span>.<span style="color:#a6e22e">ID</span>, <span style="color:#a6e22e">result</span>, <span style="color:#66d9ef">false</span>))
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">resp</span>.<span style="color:#a6e22e">StopReason</span> <span style="color:#f92672">!=</span> <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">StopReasonToolUse</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">break</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// All results from this turn go back in ONE user message.</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">messages</span> = append(<span style="color:#a6e22e">messages</span>, <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">NewUserMessage</span>(<span style="color:#a6e22e">toolResults</span><span style="color:#f92672">...</span>))
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Five things in there are load-bearing, and four of them are easy to get subtly wrong.</p>
<p><strong><code>resp.ToParam()</code> converts the response into a history entry.</strong> You must append the assistant&rsquo;s turn — including its tool-call blocks — before you send the results, or the next request has results referring to a call that does not exist in the conversation.</p>
<p><strong>Parse the tool input; never pattern-match the raw string.</strong> <code>variant.JSON.Input.Raw()</code> gives you the JSON to unmarshal. Current models vary their JSON string escaping (Unicode escapes, escaped forward slashes), so anything doing <code>strings.Contains</code> on the serialised input is a bug waiting for a release.</p>
<p><strong>All tool results go back in a single user message.</strong> <code>anthropic.NewUserMessage</code> is variadic for exactly this reason. Splitting results across several messages technically works, and it quietly teaches the model to stop issuing parallel calls — which halves your throughput for no visible reason.</p>
<p><strong><code>StopReason</code> is the exit condition</strong>, not &ldquo;did I see any tool blocks&rdquo;. Check it after you have appended the results, not before.</p>
<p><strong>Every tool call needs a result.</strong> If the model asked for three tools and you return two results, the next request is malformed. Including for the one that failed — which brings us to the most useful trick in this whole article.</p>
<h2 id="errors-are-results-not-exceptions">Errors Are Results, Not Exceptions</h2>
<p>The instinct when a tool fails is to abort the loop. Usually that is wrong. Hand the failure back to the model as a tool result flagged as an error, and it will very often recover on its own — retry with a corrected argument, try a different tool, or tell the user what went wrong:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">out</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">h</span>.<span style="color:#a6e22e">run</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">variant</span>)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// isError = true. The model sees the failure and can adapt.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">toolResults</span> = append(<span style="color:#a6e22e">toolResults</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">NewToolResultBlock</span>(<span style="color:#a6e22e">block</span>.<span style="color:#a6e22e">ID</span>, <span style="color:#a6e22e">err</span>.<span style="color:#a6e22e">Error</span>(), <span style="color:#66d9ef">true</span>))
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">continue</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">toolResults</span> = append(<span style="color:#a6e22e">toolResults</span>, <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">NewToolResultBlock</span>(<span style="color:#a6e22e">block</span>.<span style="color:#a6e22e">ID</span>, <span style="color:#a6e22e">out</span>, <span style="color:#66d9ef">false</span>))
</span></span></code></pre></div><p>That third parameter is <code>isError</code>. Getting this right turns a class of hard failures into self-correcting ones.</p>
<p>One caveat worth stating plainly: the error text goes into the model&rsquo;s context, so do not put a raw database error with connection strings and internal hostnames in there. Return the error you would show a careful external user. This is the same discipline as deciding what a sentinel error exposes at your HTTP boundary, which I covered in <a href="/posts/error-handling-in-go/">error handling in Go</a>.</p>
<h2 id="let-the-sdk-drive">Let the SDK Drive</h2>
<p>Once you understand the loop, you mostly do not want to maintain it. The Go SDK&rsquo;s tool runner handles the iteration, and generates the JSON schema from your struct tags:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">import</span> <span style="color:#e6db74">&#34;github.com/anthropics/anthropic-sdk-go/toolrunner&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">GetWeatherInput</span> <span style="color:#66d9ef">struct</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">City</span> <span style="color:#66d9ef">string</span> <span style="color:#e6db74">`json:&#34;city&#34; jsonschema:&#34;required,description=The city name&#34;`</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">weatherTool</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">toolrunner</span>.<span style="color:#a6e22e">NewBetaToolFromJSONSchema</span>(
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;get_weather&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;Get current weather for a city&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">ctx</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Context</span>, <span style="color:#a6e22e">in</span> <span style="color:#a6e22e">GetWeatherInput</span>) (<span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">BetaToolResultBlockParamContentUnion</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">BetaToolResultBlockParamContentUnion</span>{
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">OfText</span>: <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">BetaTextBlockParam</span>{
</span></span><span style="display:flex;"><span>                <span style="color:#a6e22e">Text</span>: <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Sprintf</span>(<span style="color:#e6db74">&#34;The weather in %s is sunny, 22°C&#34;</span>, <span style="color:#a6e22e">in</span>.<span style="color:#a6e22e">City</span>),
</span></span><span style="display:flex;"><span>            },
</span></span><span style="display:flex;"><span>        }, <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">runner</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">client</span>.<span style="color:#a6e22e">Beta</span>.<span style="color:#a6e22e">Messages</span>.<span style="color:#a6e22e">NewToolRunner</span>(
</span></span><span style="display:flex;"><span>    []<span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">BetaTool</span>{<span style="color:#a6e22e">weatherTool</span>},
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">BetaToolRunnerParams</span>{
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">BetaMessageNewParams</span>: <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">BetaMessageNewParams</span>{
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">Model</span>:     <span style="color:#e6db74">&#34;claude-opus-5&#34;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">MaxTokens</span>: <span style="color:#ae81ff">16000</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">Messages</span>: []<span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">BetaMessageParam</span>{
</span></span><span style="display:flex;"><span>                <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">NewBetaUserMessage</span>(<span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">NewBetaTextBlock</span>(<span style="color:#e6db74">&#34;What&#39;s the weather in Kyiv?&#34;</span>)),
</span></span><span style="display:flex;"><span>            },
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">MaxIterations</span>: <span style="color:#ae81ff">5</span>,
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">message</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">runner</span>.<span style="color:#a6e22e">RunToCompletion</span>(<span style="color:#a6e22e">ctx</span>)
</span></span></code></pre></div><p>Note the namespace: this lives under <code>client.Beta.Messages</code>, and the types are the <code>Beta*</code> variants — <code>BetaTextBlock</code>, not <code>TextBlock</code>. Mixing the two is the most common compile error here.</p>
<p><code>MaxIterations</code> is not optional decoration. Without a ceiling, a model that gets into a retry rut can loop until your context deadline, and you pay for every turn. Set it to the smallest number that lets legitimate work finish.</p>
<p>If you need to inspect or gate each step — approvals, audit logging, a check before a destructive tool runs — you do not have to drop back to a manual loop. The runner exposes <code>NextMessage()</code> and an <code>All()</code> iterator so you can step it and look at each message, and its <code>Params</code> field lets you adjust the next request. Reach for the manual loop only when you want control the runner genuinely does not expose.</p>
<h2 id="running-tools-concurrently--with-a-limit">Running Tools Concurrently — With a Limit</h2>
<p>When the model asks for four tools in one turn, running them sequentially wastes the whole point. But the naive concurrent version is the same mistake Go developers make everywhere else:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// Don&#39;t. One turn can ask for many tools; this has no ceiling.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">for</span> <span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">call</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">calls</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">go</span> <span style="color:#a6e22e">run</span>(<span style="color:#a6e22e">call</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Use a bounded group. The results still have to come back in one message, in a fixed order, so index into a preallocated slice rather than appending from goroutines:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">import</span> <span style="color:#e6db74">&#34;golang.org/x/sync/errgroup&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">results</span> <span style="color:#f92672">:=</span> make([]<span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">ContentBlockParamUnion</span>, len(<span style="color:#a6e22e">calls</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">g</span>, <span style="color:#a6e22e">gctx</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">errgroup</span>.<span style="color:#a6e22e">WithContext</span>(<span style="color:#a6e22e">ctx</span>)
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">g</span>.<span style="color:#a6e22e">SetLimit</span>(<span style="color:#ae81ff">4</span>) <span style="color:#75715e">// whatever your slowest downstream can absorb</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span>, <span style="color:#a6e22e">call</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">calls</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">g</span>.<span style="color:#a6e22e">Go</span>(<span style="color:#66d9ef">func</span>() <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">out</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">h</span>.<span style="color:#a6e22e">run</span>(<span style="color:#a6e22e">gctx</span>, <span style="color:#a6e22e">call</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#75715e">// Not a group error: hand it to the model instead.</span>
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">results</span>[<span style="color:#a6e22e">i</span>] = <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">NewToolResultBlock</span>(<span style="color:#a6e22e">call</span>.<span style="color:#a6e22e">ID</span>, <span style="color:#a6e22e">err</span>.<span style="color:#a6e22e">Error</span>(), <span style="color:#66d9ef">true</span>)
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">results</span>[<span style="color:#a6e22e">i</span>] = <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">NewToolResultBlock</span>(<span style="color:#a6e22e">call</span>.<span style="color:#a6e22e">ID</span>, <span style="color:#a6e22e">out</span>, <span style="color:#66d9ef">false</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>    })
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">g</span>.<span style="color:#a6e22e">Wait</span>(); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">messages</span> = append(<span style="color:#a6e22e">messages</span>, <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">NewUserMessage</span>(<span style="color:#a6e22e">results</span><span style="color:#f92672">...</span>))
</span></span></code></pre></div><p>Each goroutine writes one distinct slice element, so no mutex is needed — different elements are different memory. Appending to a shared slice from several goroutines is a different story, and so is writing to a shared map; <a href="/posts/concurrent-map-writing-and-reading-in-go/">concurrent map writing and reading in Go</a> has the failure mode in detail.</p>
<p>Notice that a tool failure returns <code>nil</code> from <code>g.Go</code>. Returning the error would cancel <code>gctx</code> and kill the sibling tool calls, when what you actually want is to report that one failure to the model and let the others finish. The full set of tradeoffs around <code>SetLimit</code> and error propagation is in <a href="/posts/worker-pools-in-go-with-errgroup/">worker pools in Go with errgroup</a>.</p>
<h2 id="designing-the-tools-themselves">Designing the Tools Themselves</h2>
<p>The loop is the easy part. Tool <em>design</em> is where agents get good or stay bad.</p>
<p><strong>The description is the API documentation, and its reader is the model.</strong> A tool called <code>search</code> described as &ldquo;searches&rdquo; will be called wrongly and often. Say what it searches, what it returns, and when <em>not</em> to use it. Most &ldquo;the agent keeps doing the wrong thing&rdquo; problems are description problems.</p>
<p><strong>Fewer, broader tools beat many narrow ones.</strong> Twenty tools that each wrap one endpoint force the model to plan a long chain and give it twenty chances to pick wrong. One <code>query_orders</code> tool with a few well-named parameters usually outperforms <code>get_order</code>, <code>list_orders_by_user</code>, <code>list_orders_by_date</code> and <code>count_orders</code>.</p>
<p><strong>Constrain the schema.</strong> Enums, required fields and explicit types are enforced before your handler runs. Every constraint you express in the schema is a class of invalid call you never have to validate by hand.</p>
<p><strong>Make results terse.</strong> Tool results occupy context on every subsequent turn of the loop. Returning a 400-row JSON dump costs you tokens on turn two, turn three and turn four. Return the fields the model needs to decide what to do next, and nothing else.</p>
<p><strong>Be deliberate about side effects.</strong> The model will call your tools in orders you did not anticipate. Anything that writes, sends, charges or deletes wants an approval gate — step the runner and confirm — or, at minimum, idempotency so a double call is harmless.</p>
<h2 id="guardrails-that-actually-matter-in-production">Guardrails That Actually Matter in Production</h2>
<p>A tool loop has a cost profile unlike a normal handler: every iteration resends the whole conversation. The bill grows quadratically with the number of turns if you are not careful, and three things keep it honest.</p>
<p><strong>Cap the iterations.</strong> <code>MaxIterations</code> on the runner, or a counter in your manual loop. Non-negotiable.</p>
<p><strong>Bound the wall clock.</strong> A deadline on the context that covers the whole loop, not each request:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">cancel</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">WithTimeout</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#ae81ff">5</span><span style="color:#f92672">*</span><span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Minute</span>)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">cancel</span>()
</span></span></code></pre></div><p><strong>Cache the prefix.</strong> Every turn resends the system prompt and the full history. Without prompt caching you pay full price for all of it, every iteration — this is where agent loops get expensive, and it is the one lever with no quality tradeoff at all. It gets <a href="/posts/prompt-caching-llm-cost/">its own article</a>.</p>
<p>Then there is the interaction between concurrency and rate limits. A pool of four tool calls, times however many concurrent user requests, times however many turns each — an agent loop is a very effective way to discover your own rate limits. The token-bucket approach from <a href="/posts/rate-limiting-go-apis/">rate limiting Go APIs</a> works just as well pointed at your own outbound calls as at inbound traffic.</p>
<h2 id="observability-or-you-are-flying-blind">Observability, Or You Are Flying Blind</h2>
<p>When a loop misbehaves, you need to see what the model actually saw. Log per iteration:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">slog</span>.<span style="color:#a6e22e">Info</span>(<span style="color:#e6db74">&#34;agent turn&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;iteration&#34;</span>, <span style="color:#a6e22e">i</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;stop_reason&#34;</span>, <span style="color:#a6e22e">resp</span>.<span style="color:#a6e22e">StopReason</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;tools_called&#34;</span>, <span style="color:#a6e22e">toolNames</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;input_tokens&#34;</span>, <span style="color:#a6e22e">resp</span>.<span style="color:#a6e22e">Usage</span>.<span style="color:#a6e22e">InputTokens</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;output_tokens&#34;</span>, <span style="color:#a6e22e">resp</span>.<span style="color:#a6e22e">Usage</span>.<span style="color:#a6e22e">OutputTokens</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;cache_read&#34;</span>, <span style="color:#a6e22e">resp</span>.<span style="color:#a6e22e">Usage</span>.<span style="color:#a6e22e">CacheReadInputTokens</span>,
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>With a request-scoped logger carrying the conversation id (<a href="/posts/structured-logging-in-go-with-slog/">the pattern from the slog post</a>), you can pull the entire trajectory of one run out of your logs — which is the difference between &ldquo;the agent is flaky&rdquo; and &ldquo;on turn three it called <code>search</code> with an empty query because the description was ambiguous&rdquo;.</p>
<h2 id="pitfalls">Pitfalls</h2>
<table>
	<thead>
			<tr>
					<th>Pitfall</th>
					<th>Fix</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Results returned for only some tool calls</td>
					<td>Return one result per <code>tool_use</code> block, failures included</td>
			</tr>
			<tr>
					<td>Tool results split across several user messages</td>
					<td>One user message, all results, variadic <code>NewUserMessage</code></td>
			</tr>
			<tr>
					<td>Assistant turn not appended before results</td>
					<td><code>messages = append(messages, resp.ToParam())</code> first</td>
			</tr>
			<tr>
					<td>String-matching the raw tool input</td>
					<td><code>json.Unmarshal(variant.JSON.Input.Raw())</code></td>
			</tr>
			<tr>
					<td>Mixing <code>TextBlock</code> and <code>BetaTextBlock</code></td>
					<td>Pick a namespace; the runner is <code>Beta.*</code> throughout</td>
			</tr>
			<tr>
					<td>Loop runs until the deadline</td>
					<td><code>MaxIterations</code>, plus a context timeout for the whole loop</td>
			</tr>
			<tr>
					<td>Tool error cancels its siblings</td>
					<td>Return <code>nil</code> from <code>g.Go</code>; hand the error to the model</td>
			</tr>
			<tr>
					<td>Cost grows faster than expected</td>
					<td>Cache the prefix; keep tool results terse</td>
			</tr>
	</tbody>
</table>
<h2 id="conclusion">Conclusion</h2>
<p>The loop is twenty lines and you should write it once by hand, then let the runner own it. After that, the work that actually improves an agent is not loop code at all: sharper tool descriptions, tighter schemas, terser results, a hard iteration cap, and enough logging to reconstruct a bad run. The interesting engineering is in the tools, not the loop around them.</p>]]></content:encoded>
      <category>AI Engineering</category>
      <category>Go Programming</category>
      <category>Backend Development</category>
    </item>
    <item>
      <title>Worker Pools in Go: Bounded Concurrency with errgroup</title>
      <link>https://webdevstation.com/posts/worker-pools-in-go-with-errgroup/</link>
      <pubDate>Tue, 18 Aug 2026 11:20:00 +0200</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/worker-pools-in-go-with-errgroup/</guid>
      <description>Stop spawning unbounded goroutines. A practical guide to worker pools in Go using channels, sync.WaitGroup and errgroup.SetLimit — with error propagation,…</description>
      <content:encoded><![CDATA[<p>Goroutines are so cheap that the first concurrent version of anything usually looks like <code>for _, item := range items { go process(item) }</code>. That works beautifully with ten items. With fifty thousand it opens fifty thousand database connections, and the thing you were trying to speed up falls over instead. What you almost always want is a <em>bounded</em> pool: N things in flight, no more. Here is how I build them.</p>
<h2 id="the-problem-with-the-obvious-version">The Problem With the Obvious Version</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// Do not ship this.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">fetchAll</span>(<span style="color:#a6e22e">urls</span> []<span style="color:#66d9ef">string</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">wg</span> <span style="color:#a6e22e">sync</span>.<span style="color:#a6e22e">WaitGroup</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">url</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">urls</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Add</span>(<span style="color:#ae81ff">1</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">go</span> <span style="color:#66d9ef">func</span>() {
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Done</span>()
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">fetch</span>(<span style="color:#a6e22e">url</span>)
</span></span><span style="display:flex;"><span>        }()
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Wait</span>()
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Three separate problems:</p>
<ol>
<li><strong>No limit.</strong> <code>len(urls)</code> concurrent requests. The remote service rate-limits you, or your file descriptors run out, or both.</li>
<li><strong>No errors.</strong> <code>fetch</code> returns one and it goes nowhere.</li>
<li><strong>No cancellation.</strong> If the caller gives up, every goroutine keeps running to completion.</li>
</ol>
<p>The concurrency itself is not the mistake — the missing back pressure is.</p>
<h2 id="the-classic-channel-pool">The Classic Channel Pool</h2>
<p>The traditional shape is a jobs channel, a fixed number of workers reading from it, and a results channel:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">job</span> <span style="color:#66d9ef">struct</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">ID</span>  <span style="color:#66d9ef">int</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">URL</span> <span style="color:#66d9ef">string</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">result</span> <span style="color:#66d9ef">struct</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">JobID</span> <span style="color:#66d9ef">int</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Body</span>  []<span style="color:#66d9ef">byte</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Err</span>   <span style="color:#66d9ef">error</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">workerPool</span>(<span style="color:#a6e22e">ctx</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Context</span>, <span style="color:#a6e22e">jobs</span> []<span style="color:#a6e22e">job</span>, <span style="color:#a6e22e">workers</span> <span style="color:#66d9ef">int</span>) []<span style="color:#a6e22e">result</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">jobCh</span> <span style="color:#f92672">:=</span> make(<span style="color:#66d9ef">chan</span> <span style="color:#a6e22e">job</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">resCh</span> <span style="color:#f92672">:=</span> make(<span style="color:#66d9ef">chan</span> <span style="color:#a6e22e">result</span>, len(<span style="color:#a6e22e">jobs</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">wg</span> <span style="color:#a6e22e">sync</span>.<span style="color:#a6e22e">WaitGroup</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span> &lt; <span style="color:#a6e22e">workers</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Add</span>(<span style="color:#ae81ff">1</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">go</span> <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">workerID</span> <span style="color:#66d9ef">int</span>) {
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Done</span>()
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">j</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">jobCh</span> {
</span></span><span style="display:flex;"><span>                <span style="color:#a6e22e">body</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">fetch</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">j</span>.<span style="color:#a6e22e">URL</span>)
</span></span><span style="display:flex;"><span>                <span style="color:#66d9ef">select</span> {
</span></span><span style="display:flex;"><span>                <span style="color:#66d9ef">case</span> <span style="color:#a6e22e">resCh</span> <span style="color:#f92672">&lt;-</span> <span style="color:#a6e22e">result</span>{<span style="color:#a6e22e">JobID</span>: <span style="color:#a6e22e">j</span>.<span style="color:#a6e22e">ID</span>, <span style="color:#a6e22e">Body</span>: <span style="color:#a6e22e">body</span>, <span style="color:#a6e22e">Err</span>: <span style="color:#a6e22e">err</span>}:
</span></span><span style="display:flex;"><span>                <span style="color:#66d9ef">case</span> <span style="color:#f92672">&lt;-</span><span style="color:#a6e22e">ctx</span>.<span style="color:#a6e22e">Done</span>():
</span></span><span style="display:flex;"><span>                    <span style="color:#66d9ef">return</span>
</span></span><span style="display:flex;"><span>                }
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>        }(<span style="color:#a6e22e">i</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Feed the workers, stopping early if the caller cancels.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">go</span> <span style="color:#66d9ef">func</span>() {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">defer</span> close(<span style="color:#a6e22e">jobCh</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">j</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">jobs</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">select</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">case</span> <span style="color:#a6e22e">jobCh</span> <span style="color:#f92672">&lt;-</span> <span style="color:#a6e22e">j</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">case</span> <span style="color:#f92672">&lt;-</span><span style="color:#a6e22e">ctx</span>.<span style="color:#a6e22e">Done</span>():
</span></span><span style="display:flex;"><span>                <span style="color:#66d9ef">return</span>
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    }()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Wait</span>()
</span></span><span style="display:flex;"><span>    close(<span style="color:#a6e22e">resCh</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">out</span> <span style="color:#f92672">:=</span> make([]<span style="color:#a6e22e">result</span>, <span style="color:#ae81ff">0</span>, len(<span style="color:#a6e22e">jobs</span>))
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">r</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">resCh</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">out</span> = append(<span style="color:#a6e22e">out</span>, <span style="color:#a6e22e">r</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">out</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>This is worth understanding because you will read it in a lot of codebases, and because it shows the mechanics plainly. Note two things that are easy to get wrong:</p>
<ul>
<li><strong><code>close(jobCh)</code> is the workers&rsquo; exit signal.</strong> <code>for j := range jobCh</code> ends when the channel closes. Forget the close and <code>wg.Wait()</code> blocks forever.</li>
<li><strong>Every channel send is paired with <code>&lt;-ctx.Done()</code>.</strong> Without that, a worker sending to a full <code>resCh</code> that nobody is reading leaks for the lifetime of the process.</li>
</ul>
<p>It is also about forty lines to do something the standard extended library does in eight.</p>
<h2 id="the-errgroup-version">The errgroup Version</h2>
<p><code>golang.org/x/sync/errgroup</code> is a <code>sync.WaitGroup</code> that also collects the first error and cancels its siblings. <code>SetLimit</code> turns it into a bounded pool:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">import</span> <span style="color:#e6db74">&#34;golang.org/x/sync/errgroup&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">fetchAll</span>(<span style="color:#a6e22e">ctx</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Context</span>, <span style="color:#a6e22e">urls</span> []<span style="color:#66d9ef">string</span>) ([][]<span style="color:#66d9ef">byte</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">bodies</span> <span style="color:#f92672">:=</span> make([][]<span style="color:#66d9ef">byte</span>, len(<span style="color:#a6e22e">urls</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">g</span>, <span style="color:#a6e22e">ctx</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">errgroup</span>.<span style="color:#a6e22e">WithContext</span>(<span style="color:#a6e22e">ctx</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">g</span>.<span style="color:#a6e22e">SetLimit</span>(<span style="color:#ae81ff">10</span>) <span style="color:#75715e">// at most 10 in flight</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span>, <span style="color:#a6e22e">url</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">urls</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">g</span>.<span style="color:#a6e22e">Go</span>(<span style="color:#66d9ef">func</span>() <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">body</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">fetch</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">url</span>)
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>                <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Errorf</span>(<span style="color:#e6db74">&#34;fetch %s: %w&#34;</span>, <span style="color:#a6e22e">url</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>            <span style="color:#75715e">// Each goroutine owns exactly one slot: no mutex needed.</span>
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">bodies</span>[<span style="color:#a6e22e">i</span>] = <span style="color:#a6e22e">body</span>
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>        })
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">g</span>.<span style="color:#a6e22e">Wait</span>(); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>, <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">bodies</span>, <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>That is the whole pool. Behaviour worth knowing:</p>
<ul>
<li><strong><code>g.Go</code> blocks</strong> once the limit is reached, until a slot frees up. The <code>for</code> loop becomes its own back pressure — no jobs channel needed.</li>
<li><strong><code>errgroup.WithContext</code> returns a derived context</strong> that is cancelled the moment any goroutine returns a non-nil error. Shadowing <code>ctx</code> with it, as above, is deliberate: every <code>fetch</code> gets the cancellable one.</li>
<li><strong><code>g.Wait()</code> returns the first error</strong>, and waits for the rest regardless. Later errors are discarded — if you need all of them, collect them yourself (<code>errors.Join</code> is a good fit, see <a href="/posts/error-handling-in-go/">error handling in Go</a>).</li>
<li><strong>Writing to <code>bodies[i]</code></strong> is safe without a mutex because each goroutine writes one distinct element. Different elements of a slice are different memory; that is not a data race. Appending to a shared slice, or writing to a shared map, absolutely is — see <a href="/posts/concurrent-map-writing-and-reading-in-go/">concurrent map writing and reading in Go</a> for what that failure looks like.</li>
</ul>
<h3 id="a-note-on-loop-variables">A Note on Loop Variables</h3>
<p>The example above relies on Go 1.22&rsquo;s per-iteration loop variables. Before 1.22, <code>i</code> and <code>url</code> were shared across iterations and every goroutine would see the final values — the single most common concurrency bug in Go. On older versions you must copy them:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span>, <span style="color:#a6e22e">url</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">urls</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">i</span>, <span style="color:#a6e22e">url</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">i</span>, <span style="color:#a6e22e">url</span> <span style="color:#75715e">// required before Go 1.22</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">g</span>.<span style="color:#a6e22e">Go</span>(<span style="color:#66d9ef">func</span>() <span style="color:#66d9ef">error</span> { <span style="color:#75715e">/* ... */</span> })
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Since Go 1.22 the copy is unnecessary. Leaving it in is harmless, and I still write it in code that must build on older toolchains. The loop semantics change was one of the more consequential recent additions to the language — I touched on the surrounding rules in <a href="/posts/mastering-for-loops-in-go/">mastering Golang for loops</a>.</p>
<h2 id="streaming-results-instead-of-preallocating">Streaming Results Instead of Preallocating</h2>
<p>Indexing into a preallocated slice only works when you know the number of jobs up front. For a stream, send results down a channel and read them concurrently:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">processStream</span>(<span style="color:#a6e22e">ctx</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Context</span>, <span style="color:#a6e22e">in</span> <span style="color:#f92672">&lt;-</span><span style="color:#66d9ef">chan</span> <span style="color:#a6e22e">job</span>, <span style="color:#a6e22e">workers</span> <span style="color:#66d9ef">int</span>) (<span style="color:#f92672">&lt;-</span><span style="color:#66d9ef">chan</span> <span style="color:#a6e22e">result</span>, <span style="color:#66d9ef">func</span>() <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">out</span> <span style="color:#f92672">:=</span> make(<span style="color:#66d9ef">chan</span> <span style="color:#a6e22e">result</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">g</span>, <span style="color:#a6e22e">ctx</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">errgroup</span>.<span style="color:#a6e22e">WithContext</span>(<span style="color:#a6e22e">ctx</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">g</span>.<span style="color:#a6e22e">SetLimit</span>(<span style="color:#a6e22e">workers</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">go</span> <span style="color:#66d9ef">func</span>() {
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// Closing `out` after every worker has finished lets the consumer</span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// range over it and stop naturally.</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">defer</span> close(<span style="color:#a6e22e">out</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">j</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">in</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">g</span>.<span style="color:#a6e22e">Go</span>(<span style="color:#66d9ef">func</span>() <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span>                <span style="color:#a6e22e">body</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">fetch</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">j</span>.<span style="color:#a6e22e">URL</span>)
</span></span><span style="display:flex;"><span>                <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>                    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Errorf</span>(<span style="color:#e6db74">&#34;job %d: %w&#34;</span>, <span style="color:#a6e22e">j</span>.<span style="color:#a6e22e">ID</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>                }
</span></span><span style="display:flex;"><span>                <span style="color:#66d9ef">select</span> {
</span></span><span style="display:flex;"><span>                <span style="color:#66d9ef">case</span> <span style="color:#a6e22e">out</span> <span style="color:#f92672">&lt;-</span> <span style="color:#a6e22e">result</span>{<span style="color:#a6e22e">JobID</span>: <span style="color:#a6e22e">j</span>.<span style="color:#a6e22e">ID</span>, <span style="color:#a6e22e">Body</span>: <span style="color:#a6e22e">body</span>}:
</span></span><span style="display:flex;"><span>                    <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>                <span style="color:#66d9ef">case</span> <span style="color:#f92672">&lt;-</span><span style="color:#a6e22e">ctx</span>.<span style="color:#a6e22e">Done</span>():
</span></span><span style="display:flex;"><span>                    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">ctx</span>.<span style="color:#a6e22e">Err</span>()
</span></span><span style="display:flex;"><span>                }
</span></span><span style="display:flex;"><span>            })
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">_</span> = <span style="color:#a6e22e">g</span>.<span style="color:#a6e22e">Wait</span>()
</span></span><span style="display:flex;"><span>    }()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">out</span>, <span style="color:#a6e22e">g</span>.<span style="color:#a6e22e">Wait</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The consumer ranges over <code>out</code> and then calls the returned function to get the error:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">results</span>, <span style="color:#a6e22e">wait</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">processStream</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">jobs</span>, <span style="color:#ae81ff">8</span>)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">for</span> <span style="color:#a6e22e">r</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">results</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">save</span>(<span style="color:#a6e22e">r</span>)
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">wait</span>(); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Errorf</span>(<span style="color:#e6db74">&#34;process stream: %w&#34;</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p><code>g.Wait()</code> is safe to call more than once — subsequent calls return the same error immediately.</p>
<h2 id="picking-the-limit">Picking the Limit</h2>
<p>There is no universal number, but there is a reliable way to think about it.</p>
<p><strong>CPU-bound work</strong> — parsing, hashing, image resizing, compression — saturates at roughly the number of cores. More goroutines just add scheduling overhead:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">g</span>.<span style="color:#a6e22e">SetLimit</span>(<span style="color:#a6e22e">runtime</span>.<span style="color:#a6e22e">GOMAXPROCS</span>(<span style="color:#ae81ff">0</span>))
</span></span></code></pre></div><p><strong>I/O-bound work</strong> — HTTP calls, database queries, object storage — spends most of its time waiting, so the useful limit is much higher. But it is not &ldquo;as high as possible&rdquo;: it is whatever the <em>slowest downstream dependency</em> can absorb. If your database pool has 25 connections, a pool of 200 workers means 175 goroutines queueing on a mutex inside <code>database/sql</code> while your latency graph climbs.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// Match the constraint that actually binds.</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">g</span>.<span style="color:#a6e22e">SetLimit</span>(<span style="color:#a6e22e">db</span>.<span style="color:#a6e22e">Stats</span>().<span style="color:#a6e22e">MaxOpenConnections</span>)
</span></span></code></pre></div><p>For outbound HTTP, remember that Go&rsquo;s default transport keeps only <strong>2</strong> idle connections per host. Exceed that and you are opening a fresh TCP connection — plus a TLS handshake — for each extra request:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">transport</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">DefaultTransport</span>.(<span style="color:#f92672">*</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Transport</span>).<span style="color:#a6e22e">Clone</span>()
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">transport</span>.<span style="color:#a6e22e">MaxIdleConnsPerHost</span> = <span style="color:#ae81ff">50</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">transport</span>.<span style="color:#a6e22e">MaxConnsPerHost</span> = <span style="color:#ae81ff">50</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">client</span> <span style="color:#f92672">:=</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Client</span>{<span style="color:#a6e22e">Transport</span>: <span style="color:#a6e22e">transport</span>, <span style="color:#a6e22e">Timeout</span>: <span style="color:#ae81ff">10</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>}
</span></span></code></pre></div><p>Then set the pool limit to match. Tuning one without the other gets you nothing.</p>
<p>Whatever you pick, measure it. Run the job at 5, 10, 25 and 50 and look at total wall time <em>and</em> downstream latency — the fastest setting for your batch is often the one that makes everything else on the system slower. Load testing is the honest way to find out; I wrote about a lightweight setup in <a href="/posts/an-easy-way-to-loadtest-your-web-apps/">an easy way to load test your web apps</a>.</p>
<h2 id="not-every-goroutine-belongs-in-a-pool">Not Every Goroutine Belongs in a Pool</h2>
<p>A pool is for a <em>batch of similar work</em>. Some situations want something else:</p>
<p><strong>Waiting on several different things at once</strong> — no limit needed, just a group:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">g</span>, <span style="color:#a6e22e">ctx</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">errgroup</span>.<span style="color:#a6e22e">WithContext</span>(<span style="color:#a6e22e">ctx</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">user</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">User</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">orders</span> []<span style="color:#a6e22e">Order</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">g</span>.<span style="color:#a6e22e">Go</span>(<span style="color:#66d9ef">func</span>() (<span style="color:#a6e22e">err</span> <span style="color:#66d9ef">error</span>) { <span style="color:#a6e22e">user</span>, <span style="color:#a6e22e">err</span> = <span style="color:#a6e22e">loadUser</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">id</span>); <span style="color:#66d9ef">return</span> })
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">g</span>.<span style="color:#a6e22e">Go</span>(<span style="color:#66d9ef">func</span>() (<span style="color:#a6e22e">err</span> <span style="color:#66d9ef">error</span>) { <span style="color:#a6e22e">orders</span>, <span style="color:#a6e22e">err</span> = <span style="color:#a6e22e">loadOrders</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">id</span>); <span style="color:#66d9ef">return</span> })
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">g</span>.<span style="color:#a6e22e">Wait</span>(); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>, <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Errorf</span>(<span style="color:#e6db74">&#34;load profile: %w&#34;</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Three sequential 100ms calls become one 100ms call. This is the highest-value use of <code>errgroup</code> in a typical request handler, and it needs no pool at all.</p>
<p><strong>Work that must happen in order</strong> — a pool is the wrong shape entirely; you want a single consumer, like the <a href="/posts/simple-queue-implementation-in-golang/">simple queue implementation</a> I wrote about earlier.</p>
<p><strong>Fire-and-forget background work</strong> — resist it. A goroutine started in a request handler outlives the request, holds whatever it captured, and will be killed mid-flight when the process shuts down. If it matters, it belongs in a durable queue; if it does not, do it inline. The same reasoning applies at shutdown time, which I covered in <a href="/posts/graceful-shutdown-in-go-web-services/">graceful shutdown in Go web services</a>.</p>
<p><strong>Only trying if there is capacity</strong> — <code>TryGo</code> starts the goroutine only if a slot is free, and reports whether it did:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">if</span> !<span style="color:#a6e22e">g</span>.<span style="color:#a6e22e">TryGo</span>(<span style="color:#66d9ef">func</span>() <span style="color:#66d9ef">error</span> { <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">prefetch</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">url</span>) }) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Pool is busy; skip this optional work rather than blocking.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">metrics</span>.<span style="color:#a6e22e">PrefetchSkipped</span>.<span style="color:#a6e22e">Inc</span>()
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="pitfalls">Pitfalls</h2>
<table>
	<thead>
			<tr>
					<th>Pitfall</th>
					<th>Fix</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>g.Go</code> never returns</td>
					<td>Something inside blocks forever — give every call a context and a timeout</td>
			</tr>
			<tr>
					<td>Results come back in the wrong order</td>
					<td>Index into a preallocated slice, or sort by an explicit sequence number</td>
			</tr>
			<tr>
					<td><code>panic</code> in a worker kills the process</td>
					<td>Recover inside the goroutine and convert it to an error</td>
			</tr>
			<tr>
					<td>Errors vanish</td>
					<td>Return them from <code>g.Go</code>; do not just log them</td>
			</tr>
			<tr>
					<td><code>SetLimit</code> called after <code>g.Go</code></td>
					<td>Panics — set the limit before starting any work</td>
			</tr>
			<tr>
					<td>Unbounded jobs channel eats memory</td>
					<td>Use an unbuffered channel, or let <code>SetLimit</code> provide the back pressure</td>
			</tr>
			<tr>
					<td>Shared map written from workers</td>
					<td><code>sync.Map</code>, a mutex, or per-worker maps merged at the end</td>
			</tr>
	</tbody>
</table>
<p>Panic recovery is worth spelling out, because one bad input taking down the whole process is a common way for batch jobs to fail:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">g</span>.<span style="color:#a6e22e">Go</span>(<span style="color:#66d9ef">func</span>() (<span style="color:#a6e22e">err</span> <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">defer</span> <span style="color:#66d9ef">func</span>() {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">r</span> <span style="color:#f92672">:=</span> recover(); <span style="color:#a6e22e">r</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">err</span> = <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Errorf</span>(<span style="color:#e6db74">&#34;panic processing %s: %v&#34;</span>, <span style="color:#a6e22e">url</span>, <span style="color:#a6e22e">r</span>)
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    }()
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">process</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">url</span>)
</span></span><span style="display:flex;"><span>})
</span></span></code></pre></div><p>The named return value <code>err</code> is what makes this work — the deferred function assigns to it after the panic is recovered.</p>
<h2 id="conclusion">Conclusion</h2>
<p>The pattern is small: pick a limit that matches your real bottleneck, use <code>errgroup.WithContext</code> so failures cancel their siblings, return errors instead of logging them, and give every blocking operation a context. Most of the time that is eight lines and no channel plumbing at all. Save the hand-rolled channel pool for the cases where you genuinely need to stream results or vary the shape of the work — and when you do write one, remember to close the jobs channel.</p>
<p>One place this pattern turns up more than you would expect: running the tool calls an LLM asks for, several at a time but not unboundedly — see <a href="/posts/tool-use-in-go-agent-loop/">tool use in Go</a>. And when a pool does leak a worker, Go 1.27&rsquo;s goroutine leak profile will now name it — <a href="/posts/whats-new-in-go-1-27/">what&rsquo;s new in Go 1.27</a>.</p>]]></content:encoded>
      <category>Go Programming</category>
      <category>Backend Development</category>
      <category>Performance Optimization</category>
    </item>
    <item>
      <title>Why Use Golang: 9 Compelling Reasons for Your Next Project</title>
      <link>https://webdevstation.com/posts/why-use-golang/</link>
      <pubDate>Mon, 16 Jun 2025 15:21:00 +0200</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/why-use-golang/</guid>
      <description>Wondering why use Golang? Explore nine practical reasons — speed, concurrency, tooling and more — that make Go a top choice for modern back-end systems.</description>
      <content:encoded><![CDATA[<p>If you are evaluating programming languages for a new service or migrating an existing codebase, you have probably asked the question <strong><em>“why use Golang?”</em></strong>. In this article we will unpack the concrete, business-focused benefits of Go (often called <strong>Golang</strong>) and show when choosing Go is the right strategic move.</p>
<p>Go was created at Google to solve real-world problems—fast builds, simple deployment, and effortless concurrency—without sacrificing developer happiness. Let’s dive into nine reasons <strong>why you should use Golang</strong> in 2025 and beyond.</p>
<h2 id="quick-snapshot-benefits-at-a-glance">Quick Snapshot: Benefits at a Glance</h2>
<table>
	<thead>
			<tr>
					<th>Reason</th>
					<th>Developer Impact</th>
					<th>Business Impact</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Native concurrency</td>
					<td>Easier parallel code, fewer race conditions</td>
					<td>Better CPU utilization, cost savings</td>
			</tr>
			<tr>
					<td>Fast compilation</td>
					<td>Iterate in seconds, not minutes</td>
					<td>Shorter release cycles</td>
			</tr>
			<tr>
					<td>Simple syntax</td>
					<td>Smaller learning curve</td>
					<td>Faster onboarding</td>
			</tr>
			<tr>
					<td>Robust stdlib</td>
					<td>Fewer external deps</td>
					<td>Reduced maintenance risk</td>
			</tr>
			<tr>
					<td>Single binary deploys</td>
					<td><code>scp</code> &amp; run—no runtime hassles</td>
					<td>Simplified CI/CD &amp; lower ops overhead</td>
			</tr>
			<tr>
					<td>First-class tooling</td>
					<td><code>go test</code>, <code>go vet</code>, <code>go fmt</code> built-in</td>
					<td>Higher code quality</td>
			</tr>
			<tr>
					<td>Memory safety</td>
					<td>Prevents many bugs upfront</td>
					<td>Increased uptime</td>
			</tr>
			<tr>
					<td>Growing ecosystem</td>
					<td>Mature frameworks, libs, &amp; CLIs</td>
					<td>Access to talent &amp; shared solutions</td>
			</tr>
			<tr>
					<td>Backed by giants</td>
					<td>Google, Cloudflare, Uber, etc.</td>
					<td>Long-term viability</td>
			</tr>
	</tbody>
</table>
<hr>
<h2 id="1-native-concurrency-model">1. Native Concurrency Model</h2>
<p>Go’s <strong>goroutines</strong> and <strong>channels</strong> deliver lightweight concurrency without complex thread management. Spawning 100 000 goroutines is commonplace:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">wg</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">sync</span>.<span style="color:#a6e22e">WaitGroup</span>{}
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span> &lt; <span style="color:#ae81ff">1e5</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Add</span>(<span style="color:#ae81ff">1</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">go</span> <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">id</span> <span style="color:#66d9ef">int</span>) {
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Done</span>()
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#a6e22e">id</span>)
</span></span><span style="display:flex;"><span>        }(<span style="color:#a6e22e">i</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Wait</span>()
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Compare that with traditional threads and locks and the <em>why use Golang</em> answer becomes evident—<strong>parallelism is baked into the language</strong>.</p>
<h2 id="2-blazing-fast-compilation--execution">2. Blazing-Fast Compilation &amp; Execution</h2>
<p>Go produces native machine code and compiles large projects in <strong>seconds</strong>. Faster feedback loops boost productivity, and Go’s runtime performance rivals (and often exceeds) higher-level languages like Python or Node.js.</p>
<h2 id="3-pragmatic-readable-syntax">3. Pragmatic, Readable Syntax</h2>
<p>Go deliberately avoids generics-for-everything, implicit magic, and hidden control flow. The result is code that looks similar across companies, which means <strong>reading unfamiliar Go is easy</strong>.</p>
<h2 id="4-a-batteries-included-standard-library">4. A Batteries-Included Standard Library</h2>
<p>Need an HTTP server, JSON encoder, or RSA crypto? It’s already in <code>stdlib</code>. With fewer third-party dependencies, your supply-chain attack surface shrinks.</p>
<h2 id="5-static-binaries--effortless-deployment">5. Static Binaries &amp; Effortless Deployment</h2>
<p><code>go build</code> outputs a single, statically linked binary. Drop it in a container scratch image (~10 MB) or onto a bare server—no JVM, no interpreter.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>GOOS<span style="color:#f92672">=</span>linux GOARCH<span style="color:#f92672">=</span>amd64 go build -ldflags <span style="color:#e6db74">&#34;-s -w&#34;</span> -o app
</span></span></code></pre></div><h2 id="6-first-class-tooling-out-of-the-box">6. First-Class Tooling Out-of-the-Box</h2>
<p>Formatting (<code>go fmt</code>), linting (<code>go vet</code>), profiling (<code>pprof</code>), testing, and coverage are all standard. This uniform toolchain answers <strong>why use Golang</strong> for teams who value consistency.</p>
<h2 id="7-memory-safety--predictable-gc">7. Memory Safety &amp; Predictable GC</h2>
<p>Go’s garbage collector has reached <strong>&lt;1 ms 95th percentile pause times</strong> while retaining simplicity. With escape analysis and value semantics, many allocations disappear at compile time.</p>
<h2 id="8-vibrant-ecosystem--community">8. Vibrant Ecosystem &amp; Community</h2>
<p>Frameworks like <strong>Gin</strong>, <strong>Echo</strong>, and <strong>Fiber</strong> make web development painless; <strong>gRPC</strong> and <strong>Protocol Buffers</strong> have first-class support; and cloud providers ship Go SDKs on day one.</p>
<h2 id="9-proven-in-production-by-industry-leaders">9. Proven in Production by Industry Leaders</h2>
<p>Google (of course), Netflix, Uber, Cloudflare, Stripe, and many others run latency-critical systems in Go. That track record signals Go’s staying power.</p>
<hr>
<h2 id="when-not-to-use-go">When <em>Not</em> to Use Go</h2>
<p>While this article focuses on <strong>why to use Golang</strong>, balanced engineering requires knowing its limits:</p>
<ul>
<li>No generics for higher-kinded types (though basic generics landed in Go 1.18).</li>
<li>Runtime lacks a mature GUI library.</li>
<li>Manual error handling (<code>if err != nil</code>) can feel verbose.</li>
</ul>
<p>If your workload is <strong>numerical heavy-compute</strong> with tight SIMD requirements or requires sophisticated metaprogramming, Rust or C++ may fit better.</p>
<h2 id="conclusion">Conclusion</h2>
<p>So <strong>why use Golang</strong>? Because it delivers a rare combination of developer ergonomics, runtime efficiency, and operational simplicity. From microservices to CLI tools, Go empowers teams to ship reliable software—fast.</p>
<p>Ready to give Go a try? Install it, <code>go mod init</code>, and discover firsthand why thousands of engineers choose Golang every day.</p>
<p>If that convinced you, here is where I would start: <a href="/posts/understanding-golang-context/">understanding Golang context</a> for concurrency you can cancel, <a href="/posts/error-handling-in-go/">error handling in Go</a> for the idiom that trips up most newcomers, and <a href="/posts/one-of-thee-easiest-ways-to-host-go-web-apps/">one of the easiest ways to host your Go web app</a> for getting the result online. For what the language has picked up lately, see <a href="/posts/exciting-features-in-go-1-25/">exciting features coming in Go 1.25</a>.</p>]]></content:encoded>
      <category>Go Programming</category>
      <category>Backend Development</category>
    </item>
    <item>
      <title>Understanding Golang Context: Cancellation, Timeouts, and Deadlines</title>
      <link>https://webdevstation.com/posts/understanding-golang-context/</link>
      <pubDate>Mon, 16 Jun 2025 14:01:13 +0200</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/understanding-golang-context/</guid>
      <description>Deep-dive into Golang&#39;s context package to manage cancellation, timeouts, deadlines, and request-scoped data across goroutines with practical examples and best…</description>
      <content:encoded><![CDATA[<p>When working with concurrent operations in Go, few topics are as important—and as misunderstood—as the <strong><code>context</code></strong> package. Whether you are building an HTTP API, orchestrating background workers, or integrating with external services, <em>golang context</em> is the idiomatic way to propagate cancellation signals, enforce timeouts, carry deadlines, and pass request-scoped values.</p>
<p>In this article we will demystify the <code>context</code> package, walk through common use-cases, and share production-tested best practices.</p>
<h2 id="why-context-exists">Why Context Exists</h2>
<p>Go’s lightweight goroutines make it trivial to spin up concurrent work, but once you have hundreds (or thousands) of goroutines you need a structured way to:</p>
<ol>
<li>Cancel unfinished work when a client disconnects or a parent task ends.</li>
<li>Enforce upper time bounds to prevent runaway operations.</li>
<li>Propagate deadlines deep into the call graph.</li>
<li>Attach request-level metadata (trace IDs, auth tokens, etc.) without polluting function signatures.</li>
</ol>
<p>The <code>context</code> package solves these problems with two core ideas:</p>
<ul>
<li><strong>Cancellation propagation</strong> via <code>Done()</code> channels.</li>
<li><strong>Immutable trees</strong>—each derived context references its parent, forming a hierarchy that can be cancelled from the root.</li>
</ul>
<h2 id="creating-and-cancelling-contexts">Creating and Cancelling Contexts</h2>
<p>The building blocks are <code>context.Background()</code> (or <code>context.TODO()</code>), <code>context.WithCancel</code>, <code>context.WithTimeout</code>, and <code>context.WithDeadline</code>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;context&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;fmt&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;time&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Start with a root context</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">cancel</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">WithCancel</span>(<span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Background</span>())
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">cancel</span>() <span style="color:#75715e">// always release resources</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Fire off a worker goroutine</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">go</span> <span style="color:#66d9ef">func</span>() {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">select</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">case</span> <span style="color:#f92672">&lt;-</span><span style="color:#a6e22e">ctx</span>.<span style="color:#a6e22e">Done</span>():
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;worker cancelled:&#34;</span>, <span style="color:#a6e22e">ctx</span>.<span style="color:#a6e22e">Err</span>())
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">return</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    }()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Simulate some condition that requires cancellation</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Sleep</span>(<span style="color:#ae81ff">500</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Millisecond</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">cancel</span>()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Sleep</span>(<span style="color:#ae81ff">100</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Millisecond</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Running this prints:</p>
<pre tabindex="0"><code>worker cancelled: context canceled
</code></pre><h3 id="timeout-helper">Timeout Helper</h3>
<p><code>context.WithTimeout</code> wraps <code>WithCancel</code> plus a timer:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">cancel</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">WithTimeout</span>(<span style="color:#a6e22e">parent</span>, <span style="color:#ae81ff">2</span><span style="color:#f92672">*</span><span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>)
</span></span><span style="display:flex;"><span><span style="color:#75715e">// After 2s: ctx.Err() == context.DeadlineExceeded</span>
</span></span></code></pre></div><p>Remember to <strong>always call the returned <code>cancel</code></strong>—even when the timeout expires—so the timer’s internal resources are freed.</p>
<h2 id="passing-context-down-the-call-stack">Passing Context Down the Call Stack</h2>
<p>The first parameter of every context-aware function should be <code>ctx context.Context</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">fetch</span>(<span style="color:#a6e22e">ctx</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Context</span>, <span style="color:#a6e22e">url</span> <span style="color:#66d9ef">string</span>) ([]<span style="color:#66d9ef">byte</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">req</span>, <span style="color:#a6e22e">_</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">NewRequestWithContext</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">MethodGet</span>, <span style="color:#a6e22e">url</span>, <span style="color:#66d9ef">nil</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">resp</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">DefaultClient</span>.<span style="color:#a6e22e">Do</span>(<span style="color:#a6e22e">req</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>, <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">resp</span>.<span style="color:#a6e22e">Body</span>.<span style="color:#a6e22e">Close</span>()
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">io</span>.<span style="color:#a6e22e">ReadAll</span>(<span style="color:#a6e22e">resp</span>.<span style="color:#a6e22e">Body</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>When <code>ctx</code> is cancelled upstream, <code>http.Client</code> aborts the request automatically.</p>
<h2 id="deadlines-vs-timeouts">Deadlines vs. Timeouts</h2>
<p>A <strong>deadline</strong> is an absolute moment (<code>2025-06-16T14:05:00+02:00</code>) while a <strong>timeout</strong> is a relative duration (<code>5s</code>). Internally both are implemented with <code>WithDeadline</code>, but modelling them correctly communicates intent:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">deadline</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Now</span>().<span style="color:#a6e22e">Add</span>(<span style="color:#ae81ff">5</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>)
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">cancel</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">WithDeadline</span>(<span style="color:#a6e22e">parent</span>, <span style="color:#a6e22e">deadline</span>)
</span></span></code></pre></div><h2 id="storing-values-in-context">Storing Values in Context</h2>
<p><code>context.WithValue</code> allows passing request-scoped data without modifying every function signature. Use it sparingly:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// Key type prevents collisions</span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">type</span> <span style="color:#a6e22e">key</span> <span style="color:#66d9ef">string</span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">traceIDKey</span> <span style="color:#a6e22e">key</span> = <span style="color:#e6db74">&#34;traceID&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#a6e22e">ctx</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">WithValue</span>(<span style="color:#a6e22e">parent</span>, <span style="color:#a6e22e">traceIDKey</span>, <span style="color:#e6db74">&#34;abc-123&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#75715e">// Downstream retrieval</span>
</span></span><span style="display:flex;"><span> <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">v</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">ctx</span>.<span style="color:#a6e22e">Value</span>(<span style="color:#a6e22e">traceIDKey</span>); <span style="color:#a6e22e">v</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>     <span style="color:#a6e22e">traceID</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">v</span>.(<span style="color:#66d9ef">string</span>)
</span></span><span style="display:flex;"><span>     <span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;traceID:&#34;</span>, <span style="color:#a6e22e">traceID</span>)
</span></span><span style="display:flex;"><span> }
</span></span></code></pre></div><h3 id="guidelines">Guidelines</h3>
<ol>
<li>Only store immutable, request-specific data (IDs, auth tokens).</li>
<li>Never store optional params that belong in function arguments.</li>
<li>Define unexported key types to avoid collisions across packages.</li>
</ol>
<h2 id="common-pitfalls">Common Pitfalls</h2>
<table>
	<thead>
			<tr>
					<th>Pitfall</th>
					<th>Solution</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Returning <code>nil</code> context</td>
					<td>Accept a <code>context.Context</code> argument and demand callers pass one.</td>
			</tr>
			<tr>
					<td>Forgetting to cancel</td>
					<td>Always <code>defer cancel()</code> after <code>WithCancel / WithTimeout / WithDeadline</code>.</td>
			</tr>
			<tr>
					<td>Blocking select without <code>&lt;-ctx.Done()</code></td>
					<td>Include cancellation in every <code>select</code> that may block.</td>
			</tr>
			<tr>
					<td>Misusing <code>WithValue</code> for configs</td>
					<td>Pass explicit parameters instead.</td>
			</tr>
	</tbody>
</table>
<h2 id="end-to-end-example-http-server-with-timeouts">End-to-End Example: HTTP Server with Timeouts</h2>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;context&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;fmt&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;net/http&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;time&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">srv</span> <span style="color:#f92672">:=</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Server</span>{
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">Addr</span>:         <span style="color:#e6db74">&#34;:8080&#34;</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">ReadTimeout</span>:  <span style="color:#ae81ff">3</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">WriteTimeout</span>: <span style="color:#ae81ff">5</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">Handler</span>:      <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">HandlerFunc</span>(<span style="color:#a6e22e">handler</span>),
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Shutdown gracefully on interrupt</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">go</span> <span style="color:#66d9ef">func</span>() {
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;-</span><span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">After</span>(<span style="color:#ae81ff">10</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>) <span style="color:#75715e">// Simulate interrupt</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">cancel</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">WithTimeout</span>(<span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Background</span>(), <span style="color:#ae81ff">3</span><span style="color:#f92672">*</span><span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">cancel</span>()
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">_</span> = <span style="color:#a6e22e">srv</span>.<span style="color:#a6e22e">Shutdown</span>(<span style="color:#a6e22e">ctx</span>)
</span></span><span style="display:flex;"><span>    }()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Serving on :8080&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">srv</span>.<span style="color:#a6e22e">ListenAndServe</span>(); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ErrServerClosed</span> {
</span></span><span style="display:flex;"><span>        panic(<span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Server gracefully stopped&#34;</span>)
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">handler</span>(<span style="color:#a6e22e">w</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ResponseWriter</span>, <span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Request</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// r.Context() inherits deadlines from the server</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">select</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">case</span> <span style="color:#f92672">&lt;-</span><span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">After</span>(<span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>):
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">Write</span>([]byte(<span style="color:#e6db74">&#34;done&#34;</span>))
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">case</span> <span style="color:#f92672">&lt;-</span><span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">Context</span>().<span style="color:#a6e22e">Done</span>():
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Error</span>(<span style="color:#a6e22e">w</span>, <span style="color:#e6db74">&#34;request cancelled&#34;</span>, <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusRequestTimeout</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="best-practices-checklist">Best Practices Checklist</h2>
<ul>
<li>Pass <code>context.Context</code> as the first parameter; never embed it in structs.</li>
<li>Do not store contexts—pass them along the call chain.</li>
<li>Cancel contexts to free resources early.</li>
<li>Use short-lived timeouts close to I/O boundaries rather than a single large timeout at the root.</li>
<li>Keep functions context-aware; return early on <code>&lt;-ctx.Done()</code>.</li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<p>The <em>golang context</em> package brings order to concurrent Go programs by standardising how we propagate cancellation and deadlines. Mastering it unlocks more reliable, resource-efficient applications.</p>
<p>Context shows up everywhere once you start looking for it. Three follow-ups that use it heavily: <a href="/posts/graceful-shutdown-in-go-web-services/">graceful shutdown in Go web services</a>, <a href="/posts/worker-pools-in-go-with-errgroup/">worker pools with errgroup</a>, and <a href="/posts/simple-queue-implementation-in-golang/">a simple queue implementation</a>. It also turns up in every LLM call you will ever write, since those are slow, cancellable and worth a deadline — see <a href="/posts/calling-claude-from-go/">calling an LLM from Go</a>.</p>]]></content:encoded>
      <category>Go Programming</category>
      <category>Backend Development</category>
    </item>
    <item>
      <title>Concurrent Map Writing and Reading in Go, or how to deal with the data races.</title>
      <link>https://webdevstation.com/posts/concurrent-map-writing-and-reading-in-go/</link>
      <pubDate>Fri, 16 Jul 2021 11:10:38 +0200</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/concurrent-map-writing-and-reading-in-go/</guid>
      <description>Learn how to effectively handle concurrent map operations in Go using sync.Map and mutex solutions to avoid data race conditions and improve application performance.</description>
      <content:encoded><![CDATA[<p>This time, I will show you how to work with the maps in go effectively and prevent the occurrence of the data race errors. Data races happen when several goroutines access the same resource concurrently and at least one of the accesses is a write.</p>
<p>Let&rsquo;s write a simple program, which generates a map of numbers and print them to the console:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> <span style="color:#e6db74">&#34;log&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">numbers</span> = make(<span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">int</span>]<span style="color:#66d9ef">int</span>, <span style="color:#ae81ff">100</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">generateNumbersMap</span>()
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">generateNumbersMap</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Write.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span> &lt; <span style="color:#ae81ff">100</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">numbers</span>[<span style="color:#a6e22e">i</span>] = <span style="color:#a6e22e">i</span>
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Read.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span> &lt; <span style="color:#ae81ff">100</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Print</span>(<span style="color:#a6e22e">numbers</span>[<span style="color:#a6e22e">i</span>])
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Now, if we run it with the data race detector option <code>go run -race main.go</code>, we can see the printed list of numbers in the console without any data race problems.</p>
<p>Everything seems to be good. Is it? Let&rsquo;s add some concurrency to our super complex program 😄 and see what happens:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;log&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;sync&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">numbers</span> = make(<span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">int</span>]<span style="color:#66d9ef">int</span>, <span style="color:#ae81ff">100</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">generateNumbersMap</span>()
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">generateNumbersMap</span>() {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">wg</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">sync</span>.<span style="color:#a6e22e">WaitGroup</span>{}
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Write.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span> &lt; <span style="color:#ae81ff">100</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Add</span>(<span style="color:#ae81ff">1</span>)
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">go</span> <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">i</span> <span style="color:#66d9ef">int</span>) {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Done</span>()
</span></span><span style="display:flex;"><span>			<span style="color:#a6e22e">numbers</span>[<span style="color:#a6e22e">i</span>] = <span style="color:#a6e22e">i</span>
</span></span><span style="display:flex;"><span>		}(<span style="color:#a6e22e">i</span>)
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Read.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span> &lt; <span style="color:#ae81ff">100</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Add</span>(<span style="color:#ae81ff">1</span>)
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">go</span> <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">i</span> <span style="color:#66d9ef">int</span>) {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Done</span>()
</span></span><span style="display:flex;"><span>			<span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Print</span>(<span style="color:#a6e22e">numbers</span>[<span style="color:#a6e22e">i</span>])
</span></span><span style="display:flex;"><span>		}(<span style="color:#a6e22e">i</span>)
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Wait</span>()
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>If we run it now, in the console we can notice the data race errors:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#f92672">==================</span>
</span></span><span style="display:flex;"><span>WARNING: DATA RACE
</span></span><span style="display:flex;"><span>Write at 0x00c0001241b0 by goroutine 8:
</span></span><span style="display:flex;"><span>  runtime.mapassign_fast64<span style="color:#f92672">()</span>
</span></span><span style="display:flex;"><span>      /usr/local/opt/go/libexec/src/runtime/map_fast64.go:92 +0x0
</span></span><span style="display:flex;"><span>  main.generateNumbersMap.func1<span style="color:#f92672">()</span>
</span></span><span style="display:flex;"><span>      /dev/webdevstation/blog-examples/concurent-map/main.go:68 +0xa4
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Previous write at 0x00c0001241b0 by goroutine 7:
</span></span><span style="display:flex;"><span>  runtime.mapassign_fast64<span style="color:#f92672">()</span>
</span></span><span style="display:flex;"><span>      /usr/local/opt/go/libexec/src/runtime/map_fast64.go:92 +0x0
</span></span><span style="display:flex;"><span>  main.generateNumbersMap.func1<span style="color:#f92672">()</span>
</span></span><span style="display:flex;"><span>      /dev/webdevstation/blog-examples/concurent-map/main.go:68 +0xa4
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Goroutine <span style="color:#ae81ff">8</span> <span style="color:#f92672">(</span>running<span style="color:#f92672">)</span> created at:
</span></span><span style="display:flex;"><span>  main.generateNumbersMap<span style="color:#f92672">()</span>
</span></span><span style="display:flex;"><span>      /dev/webdevstation/blog-examples/concurent-map/main.go:66 +0xb5
</span></span><span style="display:flex;"><span>  main.main<span style="color:#f92672">()</span>
</span></span><span style="display:flex;"><span>      /dev/webdevstation/blog-examples/concurent-map/main.go:33 +0x2f
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Goroutine <span style="color:#ae81ff">7</span> <span style="color:#f92672">(</span>finished<span style="color:#f92672">)</span> created at:
</span></span><span style="display:flex;"><span>  main.generateNumbersMap<span style="color:#f92672">()</span>
</span></span><span style="display:flex;"><span>      /dev/webdevstation/blog-examples/concurent-map/main.go:66 +0xb5
</span></span><span style="display:flex;"><span>  main.main<span style="color:#f92672">()</span>
</span></span><span style="display:flex;"><span>      /dev/webdevstation/blog-examples/concurent-map/main.go:33 +0x2f
</span></span><span style="display:flex;"><span><span style="color:#f92672">==================</span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">==================</span>
</span></span><span style="display:flex;"><span>WARNING: DATA RACE
</span></span><span style="display:flex;"><span>Read at 0x00c000146438 by goroutine 41:
</span></span><span style="display:flex;"><span>  main.generateNumbersMap.func2<span style="color:#f92672">()</span>
</span></span><span style="display:flex;"><span>      /dev/webdevstation/blog-examples/concurent-map/main.go:75 +0xc7
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Previous write at 0x00c000146438 by goroutine 7:
</span></span><span style="display:flex;"><span>  main.generateNumbersMap.func1<span style="color:#f92672">()</span>
</span></span><span style="display:flex;"><span>      /dev/webdevstation/blog-examples/concurent-map/main.go:68 +0xb9
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Goroutine <span style="color:#ae81ff">41</span> <span style="color:#f92672">(</span>running<span style="color:#f92672">)</span> created at:
</span></span><span style="display:flex;"><span>  main.generateNumbersMap<span style="color:#f92672">()</span>
</span></span><span style="display:flex;"><span>      /dev/webdevstation/blog-examples/concurent-map/main.go:73 +0x110
</span></span><span style="display:flex;"><span>  main.main<span style="color:#f92672">()</span>
</span></span><span style="display:flex;"><span>      /dev/webdevstation/blog-examples/concurent-map/main.go:33 +0x2f
</span></span><span style="display:flex;"><span><span style="color:#f92672">==================</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Found <span style="color:#ae81ff">2</span> data race<span style="color:#f92672">(</span>s<span style="color:#f92672">)</span>
</span></span><span style="display:flex;"><span>exit status <span style="color:#ae81ff">66</span>
</span></span></code></pre></div><p>There are several strategies that could be used to solve it.
I will show one of them. We are going to introduce a new struct that provides it&rsquo;s own mutex:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">SafeNumbers</span> <span style="color:#66d9ef">struct</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">sync</span>.<span style="color:#a6e22e">RWMutex</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">numbers</span> <span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">int</span>]<span style="color:#66d9ef">int</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>To be able to read and write items concurrently to this structure, we need to create the responsible methods:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">sn</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">SafeNumbers</span>) <span style="color:#a6e22e">Add</span>(<span style="color:#a6e22e">num</span> <span style="color:#66d9ef">int</span>) {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">sn</span>.<span style="color:#a6e22e">Lock</span>()
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">sn</span>.<span style="color:#a6e22e">Unlock</span>()
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">sn</span>.<span style="color:#a6e22e">numbers</span>[<span style="color:#a6e22e">num</span>] = <span style="color:#a6e22e">num</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Here we are basically telling to lock the numbers map, during adding of the new number to it. Other goroutines will wait until it became unlocked again.</p>
<p>And another method for reading:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">sn</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">SafeNumbers</span>) <span style="color:#a6e22e">Get</span>(<span style="color:#a6e22e">num</span> <span style="color:#66d9ef">int</span>) (<span style="color:#66d9ef">int</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">sn</span>.<span style="color:#a6e22e">RLock</span>()
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">sn</span>.<span style="color:#a6e22e">RUnlock</span>()
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">number</span>, <span style="color:#a6e22e">ok</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">sn</span>.<span style="color:#a6e22e">numbers</span>[<span style="color:#a6e22e">num</span>]; <span style="color:#a6e22e">ok</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">number</span>, <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#ae81ff">0</span>, <span style="color:#a6e22e">errors</span>.<span style="color:#a6e22e">New</span>(<span style="color:#e6db74">&#34;Number does not exists&#34;</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Next, let&rsquo;s refactor our <code>generateNumbersMap()</code> function:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">generateNumbersMap</span>() {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">wg</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">sync</span>.<span style="color:#a6e22e">WaitGroup</span>{}
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Init our &#34;safe&#34; numbers map struct.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">safeNumbers</span> <span style="color:#f92672">:=</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">SafeNumbers</span>{
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">numbers</span>: <span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">int</span>]<span style="color:#66d9ef">int</span>{},
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Write.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span> &lt; <span style="color:#ae81ff">100</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Add</span>(<span style="color:#ae81ff">1</span>)
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">go</span> <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">i</span> <span style="color:#66d9ef">int</span>) {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Done</span>()
</span></span><span style="display:flex;"><span>			<span style="color:#a6e22e">safeNumbers</span>.<span style="color:#a6e22e">Add</span>(<span style="color:#a6e22e">i</span>)
</span></span><span style="display:flex;"><span>		}(<span style="color:#a6e22e">i</span>)
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Read.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span> &lt; <span style="color:#ae81ff">100</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Add</span>(<span style="color:#ae81ff">1</span>)
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">go</span> <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">i</span> <span style="color:#66d9ef">int</span>) {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Done</span>()
</span></span><span style="display:flex;"><span>			<span style="color:#a6e22e">number</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">safeNumbers</span>.<span style="color:#a6e22e">Get</span>(<span style="color:#a6e22e">i</span>)
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>				<span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Print</span>(<span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>			} <span style="color:#66d9ef">else</span> {
</span></span><span style="display:flex;"><span>				<span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Print</span>(<span style="color:#a6e22e">number</span>)
</span></span><span style="display:flex;"><span>			}
</span></span><span style="display:flex;"><span>		}(<span style="color:#a6e22e">i</span>)
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Wait</span>()
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>If we run <code>go run -race main.go</code> now, there will be no more data race issues!</p>
<p>As I mentioned before, there also other ways to solve it. One of them is using of a special go type <code>sync.Map</code>.</p>
<p>Nevertheless, I hope this was helpful and you know now how to work safely with the maps in go. Especially, you should be careful with them when you create the web services, because every http request initiating a new goroutine.</p>
<p>As usual, the source code you can find <a href="https://github.com/alexsergivan/blog-examples/tree/master/concurent-map">here</a>.</p>
<p>If the goroutines writing to that map came from a batch of work, the next thing to fix is usually how many of them there are at once — <a href="/posts/worker-pools-in-go-with-errgroup/">worker pools in Go with errgroup</a> covers bounding that. And to cancel them cleanly when the request goes away, see <a href="/posts/understanding-golang-context/">understanding Golang context</a>.</p>]]></content:encoded>
      <category>Go Programming</category>
      <category>Performance Optimization</category>
    </item>
    <item>
      <title>Ristretto - the Most Performant Concurrent Cache Library for Go</title>
      <link>https://webdevstation.com/posts/ristretto-the-most-performant-concurrent-cache-library-for-go/</link>
      <pubDate>Tue, 02 Mar 2021 18:19:53 +0100</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/ristretto-the-most-performant-concurrent-cache-library-for-go/</guid>
      <description>Learn how to implement Ristretto, a high-performance concurrent memory caching library for Go applications. Includes code examples comparing database access with and…</description>
      <content:encoded><![CDATA[<p>Recently, I discovered a surprisingly reliable memory caching solution, which I&rsquo;m planning to use in all my further applications to increase performance. In this blog post, I will share some code examples of how you can integrate <a href="https://github.com/dgraph-io/ristretto">Ristretto</a> caching library into your application.</p>
<p><code>Ristretto is a fast, concurrent cache library built with a focus on performance and correctness.</code></p>
<p>This library was created by the Dgraph team as a contention-free cache for the Dgraph database.</p>
<p>Let&rsquo;s dive into the practical example. We are going to build a simple application that gets a list of users from the database. In the first iteration, there will be no caching layer at all. In the second iteration, we will add a Ristretto caching and compare execution time.</p>
<p>Below, you can see that I defined a <code>repository</code> package with the <code>Repository</code> interface and with <code>InMemoryRepository</code> implementation:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">repository</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> <span style="color:#e6db74">&#34;fmt&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Repository interface to handle users data.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">Repository</span> <span style="color:#66d9ef">interface</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">GetUsers</span>() <span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">int</span>]<span style="color:#66d9ef">string</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">InMemoryRepository</span> <span style="color:#66d9ef">struct</span>{}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// NewInMemoryRepository constructs and returns InMemoryRepository.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">NewInMemoryRepository</span>() <span style="color:#f92672">*</span><span style="color:#a6e22e">InMemoryRepository</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">InMemoryRepository</span>{}
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// GetUsers returns 50000 dummy users from the in-memory repository.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">InMemoryRepository</span>) <span style="color:#a6e22e">GetUsers</span>() <span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">int</span>]<span style="color:#66d9ef">string</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">users</span> <span style="color:#f92672">:=</span> make(<span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">int</span>]<span style="color:#66d9ef">string</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span><span style="color:#f92672">:=</span><span style="color:#ae81ff">1</span>; <span style="color:#a6e22e">i</span> <span style="color:#f92672">&lt;=</span> <span style="color:#ae81ff">50000</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">users</span>[<span style="color:#a6e22e">i</span>] = <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Sprintf</span>(<span style="color:#e6db74">&#34;User %d&#34;</span>, <span style="color:#a6e22e">i</span>)
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">users</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Next, we are going to call a <code>GetUsers()</code> method 100 times to simulate calling of the same function from several places in the real-world applications:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;fmt&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;github.com/alexsergivan/blog-examples/ristretto/repository&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span><span style="color:#f92672">:=</span><span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span>&lt;<span style="color:#ae81ff">100</span>; <span style="color:#a6e22e">i</span> <span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">UsersGetter</span>(<span style="color:#a6e22e">repository</span>.<span style="color:#a6e22e">NewInMemoryRepository</span>())
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">UsersGetter</span>(<span style="color:#a6e22e">repository</span> <span style="color:#a6e22e">repository</span>.<span style="color:#a6e22e">Repository</span>) {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">repository</span>.<span style="color:#a6e22e">GetUsers</span>()
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Let&rsquo;s measure how much time it takes to execute it with <code>time go run main.go</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>1.46s user
</span></span><span style="display:flex;"><span>0.34s system
</span></span><span style="display:flex;"><span>106% cpu
</span></span><span style="display:flex;"><span>1.686 total
</span></span></code></pre></div><p>Next, we are going to add a caching layer to our application.</p>
<p>Don&rsquo;t forget to get the Ristretto library:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>  go get github.com/dgraph-io/ristretto
</span></span></code></pre></div><p>Inside <code>repository</code> package we inject Ristretto cache:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">repository</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;fmt&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;github.com/dgraph-io/ristretto&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;time&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">InMemoryRepository</span> <span style="color:#66d9ef">struct</span>{
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">cache</span>        <span style="color:#f92672">*</span><span style="color:#a6e22e">ristretto</span>.<span style="color:#a6e22e">Cache</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// NewInMemoryRepository constructs and returns InMemoryRepository.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">NewInMemoryRepository</span>(<span style="color:#a6e22e">ristrettoCache</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">ristretto</span>.<span style="color:#a6e22e">Cache</span>) <span style="color:#f92672">*</span><span style="color:#a6e22e">InMemoryRepository</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">InMemoryRepository</span>{
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">cache</span>: <span style="color:#a6e22e">ristrettoCache</span>,
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// GetUsers returns 50000 dummy users from the in-memory repository.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">InMemoryRepository</span>) <span style="color:#a6e22e">GetUsers</span>() <span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">int</span>]<span style="color:#66d9ef">string</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">key</span> <span style="color:#f92672">:=</span> <span style="color:#e6db74">&#34;users&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">value</span>, <span style="color:#a6e22e">found</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">cache</span>.<span style="color:#a6e22e">Get</span>(<span style="color:#a6e22e">key</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// If the users data not cached yet, get it from the repository.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">if</span> !<span style="color:#a6e22e">found</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">users</span> <span style="color:#f92672">:=</span> make(<span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">int</span>]<span style="color:#66d9ef">string</span>)
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span><span style="color:#f92672">:=</span><span style="color:#ae81ff">1</span>; <span style="color:#a6e22e">i</span> <span style="color:#f92672">&lt;=</span> <span style="color:#ae81ff">50000</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#a6e22e">users</span>[<span style="color:#a6e22e">i</span>] = <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Sprintf</span>(<span style="color:#e6db74">&#34;User %d&#34;</span>, <span style="color:#a6e22e">i</span>)
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// Adds data to the cache for 1h.</span>
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">cache</span>.<span style="color:#a6e22e">SetWithTTL</span>(<span style="color:#a6e22e">key</span>, <span style="color:#a6e22e">users</span>, <span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">1</span><span style="color:#f92672">*</span><span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Hour</span>)
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Sleep</span>(<span style="color:#ae81ff">10</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Millisecond</span>)
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">users</span>
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">value</span>.(<span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">int</span>]<span style="color:#66d9ef">string</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Next, inside the <code>main()</code> function we initiate a new Ristretto cache and pass it to the <code>InMemoryRepository</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span>  <span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;github.com/alexsergivan/blog-examples/ristretto/repository&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;github.com/dgraph-io/ristretto&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">ristrettoCache</span>, <span style="color:#a6e22e">_</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">ristretto</span>.<span style="color:#a6e22e">NewCache</span>(<span style="color:#f92672">&amp;</span><span style="color:#a6e22e">ristretto</span>.<span style="color:#a6e22e">Config</span>{
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">NumCounters</span>: <span style="color:#ae81ff">1e7</span>,     <span style="color:#75715e">// Num keys to track frequency of (10M).</span>
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">MaxCost</span>:     <span style="color:#ae81ff">1</span> <span style="color:#f92672">&lt;&lt;</span> <span style="color:#ae81ff">30</span>, <span style="color:#75715e">// Maximum cost of cache (1GB).</span>
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">BufferItems</span>: <span style="color:#ae81ff">64</span>,      <span style="color:#75715e">// Number of keys per Get buffer.</span>
</span></span><span style="display:flex;"><span>	})
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span><span style="color:#f92672">:=</span><span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span>&lt;<span style="color:#ae81ff">100</span>; <span style="color:#a6e22e">i</span> <span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">UsersGetter</span>(<span style="color:#a6e22e">repository</span>.<span style="color:#a6e22e">NewInMemoryRepository</span>(<span style="color:#a6e22e">ristrettoCache</span>))
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">UsersGetter</span>(<span style="color:#a6e22e">repository</span> <span style="color:#a6e22e">repository</span>.<span style="color:#a6e22e">Repository</span>) {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">repository</span>.<span style="color:#a6e22e">GetUsers</span>()
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Let&rsquo;s check how much time it takes to perform the same action:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>0.29s user
</span></span><span style="display:flex;"><span>0.26s system
</span></span><span style="display:flex;"><span>147% cpu
</span></span><span style="display:flex;"><span>0.377 total
</span></span></code></pre></div><p>As you can notice, the total time is 4 times less than in the example without caching layer.</p>
<p>Despite a silly example, I hope you got an idea of how to integrate the Ristretto caching into your application and how it could improve overall performance.</p>
<p>The complete source code you can find <a href="https://github.com/alexsergivan/blog-examples/tree/master/ristretto">here</a>.</p>
<p>Ristretto is safe for concurrent use, which is exactly the problem a plain map does not solve — see <a href="/posts/concurrent-map-writing-and-reading-in-go/">concurrent map writing and reading in Go</a> if you want the failure mode in detail. I also refactored this caching layer with generics in <a href="/posts/example-of-how-generics-simplify-golang/">how Golang generics minimize the amount of code you need to write</a>. For a cache with a very different failure mode — one that costs you money rather than latency when it silently stops working — see <a href="/posts/prompt-caching-llm-cost/">prompt caching</a>.</p>]]></content:encoded>
      <category>Go Programming</category>
      <category>Performance Optimization</category>
    </item>
  </channel>
</rss>
