<?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>Goroutines on WebDevStation</title>
    <link>https://webdevstation.com/tags/goroutines/</link>
    <description>3 articles tagged Goroutines — 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, 18 Aug 2026 11:20:00 +0200</lastBuildDate>
    <atom:link href="https://webdevstation.com/tags/goroutines/index.xml" rel="self" type="application/rss+xml" />
    <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>.</p>]]></content:encoded>
      <category>Go Programming</category>
      <category>Backend Development</category>
      <category>Performance Optimization</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>A Simple Queue Implementation in Golang with channels</title>
      <link>https://webdevstation.com/posts/simple-queue-implementation-in-golang/</link>
      <pubDate>Tue, 12 Jan 2021 18:40:24 +0000</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/simple-queue-implementation-in-golang/</guid>
      <description>Learn how to implement a simple, efficient queue in Go using channels and goroutines. This practical guide shows you how to process operations sequentially with Go&#39;s…</description>
      <content:encoded><![CDATA[<p>In this post, I&rsquo;m going to show the way how we can implement a simple queue in Golang, using channels.</p>
<p>Let&rsquo;s clarify why would we ever use a queue? There many possible reasons. The most common one it&rsquo;s when
we have a list of operations (actions) and we need to process them one by one. This list either could be
static or dynamic (when new items arriving continuously).
As in the real-life, queue needed to be processed by something or someone. Imagine, a line of people in the
supermarket nearby cashier.
<div style="width:100%;height:0;padding-bottom:40%;position:relative;">
    <iframe src="https://giphy.com/embed/3o752ai3lDpXOw3yDu"
      width="100%" height="100%" style="position:absolute"
      frameBorder="0" allowFullScreen></iframe></div></p>
<p>Every customer in this line holds his own specific products and wants to
pay for them. In other words, every queue item has it own set of data with instructions. A cashier serves
his clients from a line one by one (processes queue items).
What would happen if all customers decided to pay simultaneously? Probably, something like this:
<div style="width:100%;height:0;padding-bottom:40%;position:relative;">
    <iframe src="https://giphy.com/embed/ls4p6mWzR0G9dRpwqm"
      width="100%" height="100%" style="position:absolute"
      frameBorder="0" allowFullScreen></iframe></div></p>
<p>To prevent this situation, let&rsquo;s implement a queue in Go quickly :)
First, we need to create a Queue struct and constructor 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">// Queue holds name, list of jobs and context with cancel.</span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">type</span> <span style="color:#a6e22e">Queue</span> <span style="color:#66d9ef">struct</span> {
</span></span><span style="display:flex;"><span>     <span style="color:#a6e22e">name</span>   <span style="color:#66d9ef">string</span>
</span></span><span style="display:flex;"><span>     <span style="color:#a6e22e">jobs</span>   <span style="color:#66d9ef">chan</span> <span style="color:#a6e22e">Job</span>
</span></span><span style="display:flex;"><span>     <span style="color:#a6e22e">ctx</span>    <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Context</span>
</span></span><span style="display:flex;"><span>     <span style="color:#a6e22e">cancel</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">CancelFunc</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">// Job - holds logic to perform some operations during queue execution.</span>
</span></span><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">Name</span>   <span style="color:#66d9ef">string</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Action</span> <span style="color:#66d9ef">func</span>() <span style="color:#66d9ef">error</span> <span style="color:#75715e">// A function that should be executed when the job is running.</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">// NewQueue instantiates new queue.</span>
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">func</span> <span style="color:#a6e22e">NewQueue</span>(<span style="color:#a6e22e">name</span> <span style="color:#66d9ef">string</span>) <span style="color:#f92672">*</span><span style="color:#a6e22e">Queue</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></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">Queue</span>{
</span></span><span style="display:flex;"><span>       <span style="color:#a6e22e">jobs</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">name</span>:   <span style="color:#a6e22e">name</span>,
</span></span><span style="display:flex;"><span>       <span style="color:#a6e22e">ctx</span>:    <span style="color:#a6e22e">ctx</span>,
</span></span><span style="display:flex;"><span>       <span style="color:#a6e22e">cancel</span>: <span style="color:#a6e22e">cancel</span>,
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>  }
</span></span></code></pre></div><p>As you have seen above, I&rsquo;ve declared an unbuffered channel (with no capacity) to hold Jobs in a Queue.
Channels will give us a really powerful possibility to work in a concurrent environment.
Moreover, our queue has a context with cancellation. This will help us to understand when all jobs in the queue were
finished in all goroutines and after that, we can free all resources.</p>
<p>In the code below we are going to add some methods to the <code>queue</code> struct.</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">// AddJobs adds jobs to the queue and cancels channel.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">q</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">Queue</span>) <span style="color:#a6e22e">AddJobs</span>(<span style="color:#a6e22e">jobs</span> []<span style="color:#a6e22e">Job</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:#a6e22e">wg</span>.<span style="color:#a6e22e">Add</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">for</span> <span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">job</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:#75715e">// Goroutine which adds job to the queue.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">go</span> <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">job</span> <span style="color:#a6e22e">Job</span>) {
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">q</span>.<span style="color:#a6e22e">AddJob</span>(<span style="color:#a6e22e">job</span>)
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Done</span>()
</span></span><span style="display:flex;"><span>    }(<span style="color:#a6e22e">job</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">go</span> <span style="color:#66d9ef">func</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 style="color:#75715e">// Cancel queue channel, when all goroutines were done.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">q</span>.<span style="color:#a6e22e">cancel</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">// AddJob sends job to the channel.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">q</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">Queue</span>) <span style="color:#a6e22e">AddJob</span>(<span style="color:#a6e22e">job</span> <span style="color:#a6e22e">Job</span>) {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">q</span>.<span style="color:#a6e22e">jobs</span> <span style="color:#f92672">&lt;-</span> <span style="color:#a6e22e">job</span>
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;New job %s added to %s queue&#34;</span>, <span style="color:#a6e22e">job</span>.<span style="color:#a6e22e">Name</span>, <span style="color:#a6e22e">q</span>.<span style="color:#a6e22e">name</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Next, let&rsquo;s add a <code>Run()</code> method to the <code>Job</code> struct, which should execute a job&rsquo;s action (the main purpose of the job).</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">// Run performs job execution.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">j</span> <span style="color:#a6e22e">Job</span>) <span style="color:#a6e22e">Run</span>() <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;Job running: %s&#34;</span>, <span style="color:#a6e22e">j</span>.<span style="color:#a6e22e">GetName</span>())
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">j</span>.<span style="color:#a6e22e">Action</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:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Alright, now let&rsquo;s create a worker, who will be responsible for serving our queue.</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">// Worker responsible for queue serving.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">Worker</span> <span style="color:#66d9ef">struct</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#a6e22e">Queue</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">Queue</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">// NewWorker initializes a new Worker.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">NewWorker</span>(<span style="color:#a6e22e">queue</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">Queue</span>) <span style="color:#f92672">*</span><span style="color:#a6e22e">Worker</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">return</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">Worker</span>{
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Queue</span>: <span style="color:#a6e22e">queue</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">// DoWork processes jobs from the queue (jobs channel).</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">w</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">Worker</span>) <span style="color:#a6e22e">DoWork</span>() <span style="color:#66d9ef">bool</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#66d9ef">for</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">select</span> {
</span></span><span style="display:flex;"><span>      <span style="color:#75715e">// if context was canceled.</span>
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">case</span> <span style="color:#f92672">&lt;-</span><span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">Queue</span>.<span style="color:#a6e22e">ctx</span>.<span style="color:#a6e22e">Done</span>():
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;Work done in queue %s: %s!&#34;</span>, <span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">Queue</span>.<span style="color:#a6e22e">name</span>, <span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">Queue</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 style="color:#66d9ef">true</span>
</span></span><span style="display:flex;"><span>      <span style="color:#75715e">// if job received.</span>
</span></span><span style="display:flex;"><span>      <span style="color:#66d9ef">case</span> <span style="color:#a6e22e">job</span> <span style="color:#f92672">:=</span> <span style="color:#f92672">&lt;-</span><span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">Queue</span>.<span style="color:#a6e22e">jobs</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">job</span>.<span style="color:#a6e22e">Run</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">continue</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></code></pre></div><p>Pay attention to the <code>DoWork()</code> method. There we have a for loop, which is listening for the jobs channel,
and if the channel receives a job, it executes it, by running <code>job.Run()</code>. If the context was canceled, it means,
that all jobs were executed and we can exit from the loop.</p>
<p>Now, let&rsquo;s see how we can work with just created queue functionality on a real example.
I&rsquo;m going to create a simple program, where we need to import new products into our storage.</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/queue/queue&#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:#75715e">// Our products storage.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">products</span> = []<span style="color:#66d9ef">string</span>{
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;books&#34;</span>,
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;computers&#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">// New products, which we need to add to our products storage.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">newProducts</span> <span style="color:#f92672">:=</span> []<span style="color:#66d9ef">string</span>{
</span></span><span style="display:flex;"><span>		<span style="color:#e6db74">&#34;apples&#34;</span>,
</span></span><span style="display:flex;"><span>		<span style="color:#e6db74">&#34;oranges&#34;</span>,
</span></span><span style="display:flex;"><span>		<span style="color:#e6db74">&#34;wine&#34;</span>,
</span></span><span style="display:flex;"><span>		<span style="color:#e6db74">&#34;bread&#34;</span>,
</span></span><span style="display:flex;"><span>		<span style="color:#e6db74">&#34;orange juice&#34;</span>,
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// New queue initialization.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">productsQueue</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">queue</span>.<span style="color:#a6e22e">NewQueue</span>(<span style="color:#e6db74">&#34;NewProducts&#34;</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">jobs</span> []<span style="color:#a6e22e">queue</span>.<span style="color:#a6e22e">Job</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Range over new products.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">newProduct</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">newProducts</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// We need to do this, because variables declared in for loops are passed by reference.</span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// Otherwise, our closure will always receive the last item from the newProducts.</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">product</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">newProduct</span>
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// Defining of the closure, where we add a new product to our simple storage (products slice)</span>
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">action</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">func</span>() <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#a6e22e">products</span> = append(<span style="color:#a6e22e">products</span>, <span style="color:#a6e22e">product</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:#75715e">// Append job to jobs slice.</span>
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">jobs</span> = append(<span style="color:#a6e22e">jobs</span>, <span style="color:#a6e22e">queue</span>.<span style="color:#a6e22e">Job</span>{
</span></span><span style="display:flex;"><span>			<span style="color:#a6e22e">Name</span>:   <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Sprintf</span>(<span style="color:#e6db74">&#34;Importing new product: %s&#34;</span>, <span style="color:#a6e22e">newProduct</span>),
</span></span><span style="display:flex;"><span>			<span style="color:#a6e22e">Action</span>: <span style="color:#a6e22e">action</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">// Adds jobs to the queue.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">productsQueue</span>.<span style="color:#a6e22e">AddJobs</span>(<span style="color:#a6e22e">jobs</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Defines a queue worker, which will execute our queue.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">worker</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">queue</span>.<span style="color:#a6e22e">NewWorker</span>(<span style="color:#a6e22e">productsQueue</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Execute jobs in queue.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">worker</span>.<span style="color:#a6e22e">DoWork</span>()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Prints products storage after queue execution.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Print</span>(<span style="color:#a6e22e">products</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>After executing this program, it will print out this data:</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>2021/01/12 22:10:33 New job Importing new product: apples added to NewProducts queue
</span></span><span style="display:flex;"><span>2021/01/12 22:10:33 Job running: Importing new product: apples
</span></span><span style="display:flex;"><span>2021/01/12 22:10:33 Job running: Importing new product: wine
</span></span><span style="display:flex;"><span>2021/01/12 22:10:33 Job running: Importing new product: oranges
</span></span><span style="display:flex;"><span>2021/01/12 22:10:33 Job running: Importing new product: bread
</span></span><span style="display:flex;"><span>2021/01/12 22:10:33 Job running: Importing new product: orange juice
</span></span><span style="display:flex;"><span>2021/01/12 22:10:33 New job Importing new product: orange juice added to NewProducts queue
</span></span><span style="display:flex;"><span>2021/01/12 22:10:33 New job Importing new product: oranges added to NewProducts queue
</span></span><span style="display:flex;"><span>2021/01/12 22:10:33 New job Importing new product: bread added to NewProducts queue
</span></span><span style="display:flex;"><span>2021/01/12 22:10:33 New job Importing new product: wine added to NewProducts queue
</span></span><span style="display:flex;"><span>2021/01/12 22:10:33 Work <span style="color:#66d9ef">done</span> in queue NewProducts: context canceled!
</span></span><span style="display:flex;"><span><span style="color:#f92672">[</span>books computers apples wine oranges bread orange juice<span style="color:#f92672">]</span>%
</span></span></code></pre></div><p>Please notice the last line in the logs:
<code>[books computers apples wine oranges bread orange juice]</code>
As you can see, the order is different from the order in <code>newProducts</code> slice. It happens because of goroutines nature.
Sometimes one goroutine needs more time to finish its work than another. For the scenario, when the order is important,
I will write a separate post later.</p>
<p>For now, that&rsquo;s it :) Hope it was helpful!</p>
<p>The source code you can find <a href="https://github.com/alexsergivan/blog-examples/tree/master/queue">here</a></p>
<p>A queue like this processes work in order, one item at a time. When order does not matter and you want several items in flight instead, reach for <a href="/posts/worker-pools-in-go-with-errgroup/">worker pools in Go with errgroup</a>. Either way, make sure the consumer stops cleanly on shutdown — see <a href="/posts/graceful-shutdown-in-go-web-services/">graceful shutdown in Go web services</a>.</p>]]></content:encoded>
      <category>Go Programming</category>
      <category>Backend Development</category>
    </item>
  </channel>
</rss>
