<?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>Performance on WebDevStation</title>
    <link>https://webdevstation.com/tags/performance/</link>
    <description>10 articles tagged Performance — tutorials, code examples and notes from building real systems, newest first.</description>
    <generator>Hugo</generator>
    <language>en</language>
    <managingEditor>Alex</managingEditor>
    <webMaster>Alex</webMaster>
    <copyright>© 2026 WebDevStation</copyright>
    <lastBuildDate>Tue, 01 Sep 2026 18:30:00 +0200</lastBuildDate>
    <atom:link href="https://webdevstation.com/tags/performance/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>What&#39;s New in Go 1.27: Generic Methods, JSON v2 and uuid in the Standard Library</title>
      <link>https://webdevstation.com/posts/whats-new-in-go-1-27/</link>
      <pubDate>Tue, 01 Sep 2026 18:30:00 +0200</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/whats-new-in-go-1-27/</guid>
      <description>Go 1.27 finally adds generic methods to the language, promotes encoding/json/v2 into the standard library, ships a uuid package, and makes goroutine leak profiles…</description>
      <content:encoded><![CDATA[<p>Most Go releases are a handful of library additions and a compiler that got a bit faster. Go 1.27, out in August 2026, is not that release. It changes the language — generic methods are finally here — and it lands two things people have been waiting years for: a second-generation JSON package and a <code>uuid</code> package, both in the standard library.</p>
<h2 id="generic-methods">Generic Methods</h2>
<p>This is the headline, and it is a real language change: <strong>a method declaration may now declare its own type parameters.</strong></p>
<p>Before 1.27, type parameters could only appear on a function or on the type itself. If you wanted a generic helper that logically belonged to a type, your only option was a package-level function:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// Go 1.26 and earlier: the helper lives in the package namespace,</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// even though it only makes sense for *Rand.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">N</span>[<span style="color:#a6e22e">Int</span> <span style="color:#a6e22e">intType</span>](<span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">rand</span>.<span style="color:#a6e22e">Rand</span>, <span style="color:#a6e22e">n</span> <span style="color:#a6e22e">Int</span>) <span style="color:#a6e22e">Int</span>
</span></span></code></pre></div><p>Now it can live where it belongs. <code>math/rand/v2</code> is the standard library&rsquo;s own example — it gained a generic <em>method</em> alongside the existing generic function:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#75715e">// Go 1.27</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">Rand</span>) <span style="color:#a6e22e">N</span>[<span style="color:#a6e22e">Int</span> <span style="color:#a6e22e">intType</span>](<span style="color:#a6e22e">n</span> <span style="color:#a6e22e">Int</span>) <span style="color:#a6e22e">Int</span>
</span></span></code></pre></div><p>Which means this now compiles:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#a6e22e">r</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">rand</span>.<span style="color:#a6e22e">New</span>(<span style="color:#a6e22e">rand</span>.<span style="color:#a6e22e">NewPCG</span>(<span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">2</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">d</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Duration</span> = <span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">N</span>(<span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>)  <span style="color:#75715e">// method, inferred as time.Duration</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">i</span> <span style="color:#66d9ef">int32</span>         = <span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">N</span>(int32(<span style="color:#ae81ff">100</span>))   <span style="color:#75715e">// same method, different instantiation</span>
</span></span></code></pre></div><p>The practical effect is that you can stop writing <code>pkg.DoThing(x, ...)</code> for operations that are conceptually <code>x.DoThing(...)</code>. Anywhere you have a container, a client, or a builder that needed a package-level generic helper, that helper can now be a method.</p>
<p>Two limits worth knowing before you plan a refactor around it:</p>
<ul>
<li><strong>Interfaces cannot declare type parameters on their methods</strong>, and an interface method cannot be satisfied by a generic method. So this does not give you generic interfaces through the back door; the method set of an interface is still fully concrete.</li>
<li>It is a <em>method</em> type parameter, distinct from the receiver&rsquo;s. <code>func (c *Cache[K, V]) MapTo[T any](...)</code> has three type parameters in play, two from the receiver and one from the method.</li>
</ul>
<p>If you have been writing the awkward package-level workarounds I described in <a href="/posts/example-of-how-generics-simplify-golang/">how Golang generics minimize the amount of code you need to write</a>, this is the release where a lot of that gets to move inside the type.</p>
<p>Two smaller language changes shipped alongside it. A key in a struct literal may now be <strong>any valid field selector</strong>, not just a top-level field name — which makes literals for structs with embedded types far less irritating. And function type inference is <strong>generalised to every context</strong> where a generic function is assigned to, or converted to, a matching function type, so a class of &ldquo;cannot infer&rdquo; errors simply disappears.</p>
<h2 id="encodingjsonv2">encoding/json/v2</h2>
<p>The JSON rewrite is in, as two packages:</p>
<ul>
<li><strong><code>encoding/json/v2</code></strong> — the high-level API you already know, redesigned.</li>
<li><strong><code>encoding/json/jsontext</code></strong> — lower-level syntactic processing, with an <code>Encoder</code> and <code>Decoder</code> that work in terms of <code>Token</code> and <code>Value</code> and hold a state machine guaranteeing the JSON is valid.</li>
</ul>
<p>The v2 entry points take variadic options:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">import</span> <span style="color:#e6db74">&#34;encoding/json/v2&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">b</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">json</span>.<span style="color:#a6e22e">Marshal</span>(<span style="color:#a6e22e">v</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">json</span>.<span style="color:#a6e22e">Deterministic</span>(<span style="color:#66d9ef">true</span>),          <span style="color:#75715e">// stable map ordering</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">json</span>.<span style="color:#a6e22e">OmitZeroStructFields</span>(<span style="color:#66d9ef">true</span>),
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">err</span> = <span style="color:#a6e22e">json</span>.<span style="color:#a6e22e">Unmarshal</span>(<span style="color:#a6e22e">data</span>, <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">v</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">json</span>.<span style="color:#a6e22e">RejectUnknownMembers</span>(<span style="color:#66d9ef">true</span>),   <span style="color:#75715e">// strict decoding, no silent drops</span>
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>Alongside <code>Marshal</code> and <code>Unmarshal</code> there are <code>MarshalWrite</code> / <code>UnmarshalRead</code> for an <code>io.Writer</code> / <code>io.Reader</code>, and <code>MarshalEncode</code> / <code>UnmarshalDecode</code> for driving a <code>jsontext.Encoder</code> / <code>Decoder</code> directly. That last pair is what you want for streaming, and it is a much better story than v1&rsquo;s <code>json.Decoder</code> ever was.</p>
<p><strong>The defaults are stricter</strong>, which is the whole point. v2 rejects invalid UTF-8 in strings and rejects duplicate names within an object — both of which v1 quietly accepted. That is more interoperable and it closes a genuine class of parser-differential bugs.</p>
<p><code>RejectUnknownMembers(true)</code> deserves a callout for anyone writing an API. v1&rsquo;s default of silently discarding unknown fields is how a typo in a client&rsquo;s request body turns into a field that is quietly zero, and then into a support ticket. Making that an error at the boundary is the same instinct as the sentinel errors in <a href="/posts/error-handling-in-go/">error handling in Go</a>: fail where the information is, not three layers down.</p>
<p><code>encoding/json</code> v1 is not going anywhere, and it gained a matching set of options with names that tell you exactly what they are for — <code>CallMethodsWithLegacySemantics</code>, <code>OmitEmptyWithLegacySemantics</code>, <code>FormatDurationAsNano</code>, <code>ParseTimeWithLooseRFC3339</code> and friends. That is the migration path: move to v2, and where behaviour differs in a way you depend on, opt back into the v1 semantics explicitly rather than discovering the difference in production.</p>
<p>One thing to be deliberate about: <strong>v2&rsquo;s stricter parsing is a behaviour change at your HTTP boundary.</strong> A client that has been sending duplicate keys or malformed UTF-8 and getting away with it will start getting errors. That is correct, and it is still worth knowing before you flip it on a live service. Point a load test at it first — the setup in <a href="/posts/an-easy-way-to-loadtest-your-web-apps/">an easy way to load test your web apps</a> is enough to see whether real traffic trips the new rules.</p>
<h2 id="a-uuid-package">A uuid Package</h2>
<p>There is now a <code>uuid</code> package in the standard library. The whole API is small enough to quote:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">UUID</span> [<span style="color:#ae81ff">16</span>]<span style="color:#66d9ef">byte</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">New</span>() <span style="color:#a6e22e">UUID</span>                        <span style="color:#75715e">// the recommended default</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">NewV4</span>() <span style="color:#a6e22e">UUID</span>                      <span style="color:#75715e">// random</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">NewV7</span>() <span style="color:#a6e22e">UUID</span>                      <span style="color:#75715e">// time-ordered</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">Parse</span>(<span style="color:#66d9ef">string</span>) (<span style="color:#a6e22e">UUID</span>, <span style="color:#66d9ef">error</span>)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">MustParse</span>(<span style="color:#66d9ef">string</span>) <span style="color:#a6e22e">UUID</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">Nil</span>() <span style="color:#a6e22e">UUID</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">Max</span>() <span style="color:#a6e22e">UUID</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">UUID</span>) <span style="color:#a6e22e">String</span>() <span style="color:#66d9ef">string</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">UUID</span>) <span style="color:#a6e22e">Compare</span>(<span style="color:#a6e22e">UUID</span>) <span style="color:#66d9ef">int</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">UUID</span>) <span style="color:#a6e22e">MarshalText</span>() ([]<span style="color:#66d9ef">byte</span>, <span style="color:#66d9ef">error</span>)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">UUID</span>) <span style="color:#a6e22e">AppendText</span>([]<span style="color:#66d9ef">byte</span>) ([]<span style="color:#66d9ef">byte</span>, <span style="color:#66d9ef">error</span>)
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#f92672">*</span><span style="color:#a6e22e">UUID</span>) <span style="color:#a6e22e">UnmarshalText</span>([]<span style="color:#66d9ef">byte</span>) <span style="color:#66d9ef">error</span>
</span></span></code></pre></div><p><code>NewV7</code> is the one to reach for when the UUID is a database key. Version 7 is time-ordered, so generated values sort roughly by creation time — which keeps B-tree index inserts near the right-hand edge instead of scattering them across the whole index the way v4 does. If you have ever watched write throughput degrade on a table with a random-UUID primary key, that is the problem v7 exists to solve.</p>
<p><code>Compare</code> returning an <code>int</code> means it drops straight into <code>slices.SortFunc</code>, and the <code>TextMarshaler</code> / <code>TextUnmarshaler</code> implementation means it round-trips through JSON and <code>database/sql</code> without a wrapper type.</p>
<p>The obvious question: does this kill <code>github.com/google/uuid</code>? Not immediately — plenty of code depends on its wider surface. But for the common case of &ldquo;I need a v4 or v7 UUID, and I need to parse one&rdquo;, that is now a dependency you can delete.</p>
<h2 id="goroutine-leak-profiles-generally-available">Goroutine Leak Profiles, Generally Available</h2>
<p>The <code>goroutineleak</code> profile, experimental in 1.26, is now GA in <code>runtime/pprof</code> and exposed at <code>/debug/pprof/goroutineleak</code>.</p>
<p>It reports goroutines blocked on a concurrency primitive that <strong>cannot possibly become unblocked</strong>. The detection is clever: it piggybacks on the garbage collector. If goroutine G is blocked on primitive P, and P is unreachable from any runnable goroutine — or from any goroutine those could unblock — then nothing can ever signal P, so G is leaked.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">import</span> <span style="color:#a6e22e">_</span> <span style="color:#e6db74">&#34;net/http/pprof&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// then:</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">//   go tool pprof http://localhost:6060/debug/pprof/goroutineleak</span>
</span></span></code></pre></div><p>The reachability trick is also the limitation, and the release notes say so plainly: leaks caused by blocking on a primitive that is still reachable through a global, or through a runnable goroutine&rsquo;s locals, will not be detected. It catches a large class, not all of them.</p>
<p>Still, this is the first tool that turns &ldquo;we think we have a goroutine leak somewhere&rdquo; into a list. The failure modes it finds are exactly the ones from <a href="/posts/worker-pools-in-go-with-errgroup/">worker pools in Go with errgroup</a> — a worker blocked forever on a send to a channel nobody is reading — and the ones a missing <code>&lt;-ctx.Done()</code> produces, which I wrote about in <a href="/posts/understanding-golang-context/">understanding Golang context</a>.</p>
<h2 id="free-performance">Free Performance</h2>
<p>The compiler now emits calls to <strong>size-specialised allocation routines</strong>, cutting the cost of small allocations (under 80 bytes) by up to 30%. The release notes are honest about what that means end to end: roughly <strong>1% on allocation-heavy programs</strong>, and about 60 KB more binary, regardless of workload.</p>
<p>One percent for a recompile is a good trade. If it causes you trouble, <code>GOEXPERIMENT=nosizespecializedmalloc</code> turns it off at build time — but note that escape hatch is expected to be <strong>removed in Go 1.28</strong>, so treat it as a window to file a bug, not a setting to keep.</p>
<h2 id="smaller-things-worth-knowing">Smaller Things Worth Knowing</h2>
<table>
	<thead>
			<tr>
					<th>Change</th>
					<th>Why you care</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>strings.CutLast(s, sep)</code></td>
					<td><code>Cut</code> from the right. The one you kept writing by hand with <code>LastIndex</code>.</td>
			</tr>
			<tr>
					<td><code>testing/synctest.Sleep(d)</code></td>
					<td>Sleep inside a synthetic-time bubble, without the awkward dance around the fake clock.</td>
			</tr>
			<tr>
					<td><code>hash/maphash.Hasher[T]</code> and <code>ComparableHasher[T]</code></td>
					<td>A real interface for hashing your own types — building a custom hash map stops meaning &ldquo;reimplement hashing&rdquo;.</td>
			</tr>
			<tr>
					<td><code>net/http.Server.MaxHeaderValueCount</code> (default 500)</td>
					<td>A cap on header value count, defending against header-flood requests.</td>
			</tr>
			<tr>
					<td><code>net/http.Server.DisableClientPriority</code></td>
					<td>Ignore client-supplied HTTP/2 priority hints.</td>
			</tr>
			<tr>
					<td><code>database/sql/driver.RowsColumnScanner</code></td>
					<td>Drivers can take over per-column scanning, which is how you avoid a round trip through <code>interface{}</code> for every value.</td>
			</tr>
			<tr>
					<td><code>go doc pkg@version</code></td>
					<td>Read the docs for a <em>specific</em> version without changing your module.</td>
			</tr>
			<tr>
					<td><code>go test</code> runs <code>stdversion</code> by default</td>
					<td>Catches use of stdlib symbols newer than the Go version your <code>go.mod</code> declares.</td>
			</tr>
	</tbody>
</table>
<p>That last one is quietly excellent. It is the failure mode where your code builds on your machine and breaks on a builder pinned to an older Go — the same class of problem I hit deploying this blog, where a template function that did not exist in the CI toolchain failed a build that was green locally.</p>
<h2 id="two-for-later">Two for Later</h2>
<p><strong><code>crypto/mldsa</code></strong> implements ML-DSA, the post-quantum signature scheme standardised as FIPS 204, with the three parameter sets <code>MLDSA44()</code>, <code>MLDSA65()</code> and <code>MLDSA87()</code>. Unless you have a compliance requirement you will not touch it this year, but it being in the standard library is what makes migration a normal task rather than a project.</p>
<p><strong><code>simd</code></strong> is an experimental package behind <code>GOEXPERIMENT=simd</code>, providing portable, vector-size-agnostic SIMD types like <code>Int8s</code> and <code>Float32s</code> that use hardware instructions where available. Experimental means experimental — but portable SIMD in Go is a genuinely interesting direction.</p>
<h2 id="should-you-upgrade">Should You Upgrade?</h2>
<p>Yes, and the calculus is unusually simple:</p>
<ul>
<li>The Go 1 compatibility promise holds; almost everything compiles unchanged.</li>
<li>You get ~1% on allocation-heavy code for a recompile.</li>
<li><code>goroutineleak</code> is worth the upgrade on its own for any service running long-lived goroutines.</li>
<li>Generic methods and JSON v2 are opt-in. Nothing forces you to rewrite anything.</li>
</ul>
<p>The only change that needs a moment&rsquo;s thought is JSON v2&rsquo;s stricter parsing, and only if you adopt it at a public boundary. Everything else is additive.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Generic methods close a gap that has been awkward since generics landed in 1.18, and they will quietly improve a lot of library APIs over the next year. JSON v2 is the rarer thing: a second attempt at a core package that gets to fix the defaults, with an explicit escape hatch back to the old behaviour. And a <code>uuid</code> package removes a dependency from nearly every service I have written.</p>
<p>Not bad for a release the notes describe as &ldquo;mostly implementation&rdquo;.</p>]]></content:encoded>
      <category>Go Programming</category>
      <category>Backend Development</category>
    </item>
    <item>
      <title>Prompt Caching: The Cheapest Win in Your LLM Bill</title>
      <link>https://webdevstation.com/posts/prompt-caching-llm-cost/</link>
      <pubDate>Mon, 31 Aug 2026 11:15:00 +0200</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/prompt-caching-llm-cost/</guid>
      <description>Prompt caching is a prefix match, and one stray timestamp can silently disable it. How cache breakpoints, TTLs and the usage fields actually work — and how to build…</description>
      <content:encoded><![CDATA[<p>I have written on this blog about caching database reads with <a href="/posts/ristretto-the-most-performant-concurrent-cache-library-for-go/">Ristretto</a> and about teaching <a href="/posts/how-to-make-nginx-cookie-aware/">Nginx to cache by cookie</a>. Prompt caching belongs in the same family, with one difference that makes it far more interesting: the thing you are caching costs real money per byte, and when the cache stops working, nothing breaks. No error, no alert, no failed request. Just a bigger invoice next month.</p>
<h2 id="one-invariant-everything-follows-from-it">One Invariant, Everything Follows From It</h2>
<p><strong>Prompt caching is a prefix match. Any change anywhere in the prefix invalidates everything after it.</strong></p>
<p>That is the whole model. The cache key is derived from the exact bytes of your rendered prompt up to each breakpoint. One byte different at position N — a timestamp, a reordered JSON key, an extra tool in the list — and every cached position at or after N is gone.</p>
<p>The render order matters and is fixed:</p>
<pre tabindex="0"><code>tools  →  system  →  messages
</code></pre><p>Tools render first, at position zero. That has a consequence people discover the expensive way: <strong>change the tool list and you have invalidated everything</strong>, system prompt and entire conversation included. More on that below.</p>
<h2 id="what-it-costs">What It Costs</h2>
<p>Two numbers govern whether caching pays:</p>
<ul>
<li>A cache <strong>read</strong> costs about <strong>0.1×</strong> the base input price.</li>
<li>A cache <strong>write</strong> costs <strong>1.25×</strong> for the 5-minute TTL, <strong>2×</strong> for the 1-hour TTL.</li>
</ul>
<p>So with the default 5-minute TTL, two requests already break even: <code>1.25 + 0.1 = 1.35</code> against <code>2.0</code> uncached. By the third request you are well ahead. With the 1-hour TTL you need three requests to break even, because the write costs double.</p>
<p>Which is why the TTL question is <em>not</em> &ldquo;how long do I want this cached&rdquo; but &ldquo;how far apart do requests sharing this prefix start&rdquo;:</p>
<table>
	<thead>
			<tr>
					<th>Start-to-start gap</th>
					<th>Use</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Under 5 minutes</td>
					<td>5-minute TTL. Every read refreshes the timer, so continuous traffic keeps it warm indefinitely and it is strictly cheaper.</td>
			</tr>
			<tr>
					<td>5–60 minutes</td>
					<td>1-hour TTL. This is the only window where the doubled write price earns its keep.</td>
			</tr>
			<tr>
					<td>Over an hour</td>
					<td>Neither, directly. Re-warm on a schedule or accept the cold miss.</td>
			</tr>
	</tbody>
</table>
<p>The subtlety in row one: a read refreshes the entry at no extra cost, and the lifetime is measured from the <em>start</em> of the request. A four-minute generation leaves about one minute for the next request to begin before a five-minute entry expires. For a chat endpoint under steady load, the 5-minute TTL is the right answer and the 1-hour TTL just doubles your write bill.</p>
<h2 id="making-it-work-in-go">Making It Work in Go</h2>
<p>The syntax is a <code>CacheControl</code> on the last block of whatever you want cached. Because tools render before system, a marker on the final system block caches <strong>both</strong>:</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">params</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">MessageNewParams</span>{
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Model</span>:     <span style="color:#e6db74">&#34;claude-opus-5&#34;</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">MaxTokens</span>: <span style="color:#ae81ff">16000</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Tools</span>:     <span style="color:#a6e22e">tools</span>, <span style="color:#75715e">// deterministic order — see below</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">System</span>: []<span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">TextBlockParam</span>{{
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">Text</span>:         <span style="color:#a6e22e">systemPrompt</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">CacheControl</span>: <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">NewCacheControlEphemeralParam</span>(), <span style="color:#75715e">// 5-minute default</span>
</span></span><span style="display:flex;"><span>    }},
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Messages</span>: <span style="color:#a6e22e">messages</span>,
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>For the 1-hour TTL:</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">CacheControl</span>: <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">CacheControlEphemeralParam</span>{
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">TTL</span>: <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">CacheControlEphemeralTTLTTL1h</span>,
</span></span><span style="display:flex;"><span>},
</span></span></code></pre></div><p>There is also a top-level <code>CacheControl</code> on <code>MessageNewParams</code> that automatically places a breakpoint on the last cacheable block and moves it forward as the conversation grows. For multi-turn chat that is the right default — no marker bookkeeping, and the growing history caches incrementally.</p>
<p><strong>The robust combination for anything agentic:</strong> one explicit breakpoint at the end of the static system prefix, so the expensive shared part has a guaranteed read point no matter what happens later in <code>messages</code>, plus top-level automatic caching for the growing tail.</p>
<p>You get four breakpoints per request, so there is no need to be frugal — place them at genuine stability boundaries.</p>
<h2 id="where-automatic-caching-is-the-wrong-tool">Where Automatic Caching Is the Wrong Tool</h2>
<p>Automatic placement puts the breakpoint at the very end of your prompt. When the prompt <em>ends</em> with something unique per request — a retrieved document, the user&rsquo;s actual question — that is a pure surcharge: every request writes a new cache entry that nothing will ever read.</p>
<p>The signature is unmistakable once you know it: <code>cache_creation_input_tokens</code> is non-zero on every single request, while <code>cache_read_input_tokens</code> never covers the shared prefix.</p>
<p>The fix is an explicit marker at the end of the <strong>shared</strong> portion:</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">Messages</span>: []<span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">MessageParam</span>{
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">NewUserMessage</span>(
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">TextBlockParam</span>{
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">Text</span>:         <span style="color:#a6e22e">sharedContext</span>, <span style="color:#75715e">// few-shot examples, retrieved docs</span>
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">CacheControl</span>: <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">NewCacheControlEphemeralParam</span>(),
</span></span><span style="display:flex;"><span>        },
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">NewTextBlock</span>(<span style="color:#a6e22e">userQuestion</span>), <span style="color:#75715e">// no marker — differs every time</span>
</span></span><span style="display:flex;"><span>    ),
</span></span><span style="display:flex;"><span>},
</span></span></code></pre></div><p>Same rule, restated: put the breakpoint where the prompt <em>stops</em> being shared, not where the prompt ends.</p>
<h2 id="the-minimum-prefix-which-is-not-monotonic">The Minimum Prefix, Which Is Not Monotonic</h2>
<p>A prompt shorter than the model&rsquo;s minimum will not cache — no error, no warning, <code>cache_creation_input_tokens</code> simply comes back zero. And the minimum does not move in the direction you would guess as models get newer:</p>
<table>
	<thead>
			<tr>
					<th>Model</th>
					<th style="text-align: right">Minimum</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Claude Opus 5, Fable 5</td>
					<td style="text-align: right">512 tokens</td>
			</tr>
			<tr>
					<td>Opus 4.8, Sonnet 5, Sonnet 4.6</td>
					<td style="text-align: right">1024 tokens</td>
			</tr>
			<tr>
					<td>Opus 4.7</td>
					<td style="text-align: right">2048 tokens</td>
			</tr>
			<tr>
					<td>Opus 4.6, Haiku 4.5</td>
					<td style="text-align: right">4096 tokens</td>
			</tr>
	</tbody>
</table>
<p>A 3,000-token system prompt caches on Opus 5 and Opus 4.8, and silently does not on Opus 4.6 or Haiku 4.5. If you switched models and your cache hit rate fell off a cliff, this is the first thing to check — and it cuts the other way too: moving to Opus 5 halves the Opus 4.8 minimum, so prompts that were previously too short start caching with no code change at all.</p>
<h2 id="silent-invalidators">Silent Invalidators</h2>
<p>This is the part worth committing to memory, because every one of these is code that looks perfectly reasonable in review.</p>
<table>
	<thead>
			<tr>
					<th>Pattern</th>
					<th>Why it kills the cache</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>time.Now()</code> in the system prompt</td>
					<td>The prefix differs on every single request</td>
			</tr>
			<tr>
					<td>A request ID or UUID early in the content</td>
					<td>Same — every request is unique</td>
			</tr>
			<tr>
					<td><code>json.Marshal</code> of a <code>map</code> in the prompt</td>
					<td>Go randomises map iteration order; the bytes differ run to run</td>
			</tr>
			<tr>
					<td>Ranging over a map to build tool definitions</td>
					<td>Same problem, at position zero, which is the worst place for it</td>
			</tr>
			<tr>
					<td>User or session ID interpolated into the system prompt</td>
					<td>A per-user prefix; nothing shares anything</td>
			</tr>
			<tr>
					<td><code>if flag { system += ... }</code></td>
					<td>Every flag combination is a distinct prefix</td>
			</tr>
			<tr>
					<td>A tool set that varies per user or per mode</td>
					<td>Tools render first — nothing caches across users</td>
			</tr>
	</tbody>
</table>
<p>The Go-specific ones deserve emphasis. Map iteration order in Go is deliberately randomised, so this is not a cache bug that appears under load — it appears on <strong>every request</strong>, and it is invisible because the rendered prompt is semantically identical each time:</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">// Bad: iteration order is randomised, so the bytes differ every run.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">for</span> <span style="color:#a6e22e">name</span>, <span style="color:#a6e22e">def</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">h</span>.<span style="color:#a6e22e">tools</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">tools</span> = append(<span style="color:#a6e22e">tools</span>, <span style="color:#a6e22e">def</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">// Good: deterministic.</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">names</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">slices</span>.<span style="color:#a6e22e">Sorted</span>(<span style="color:#a6e22e">maps</span>.<span style="color:#a6e22e">Keys</span>(<span style="color:#a6e22e">h</span>.<span style="color:#a6e22e">tools</span>))
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">for</span> <span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">name</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">names</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">tools</span> = append(<span style="color:#a6e22e">tools</span>, <span style="color:#a6e22e">h</span>.<span style="color:#a6e22e">tools</span>[<span style="color:#a6e22e">name</span>])
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Sorting keys is a one-line fix that most Go LLM code needs and almost none has.</p>
<h2 id="injecting-dynamic-context-without-breaking-everything">Injecting Dynamic Context Without Breaking Everything</h2>
<p>The usual reason a system prompt has a timestamp in it is that the model genuinely needs to know the date, or the user&rsquo;s plan tier, or the current mode. The instinct is to template it into the system prompt. Don&rsquo;t — that is the front of the prefix, and it invalidates everything behind it.</p>
<p>Put dynamic context <strong>after</strong> the cached history instead. On the newest models there is a first-class channel for this: a <code>system</code>-role message appended to <code>messages</code>, rather than an edit to the top-level <code>system</code> field.</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">// The top-level system prompt stays byte-identical and stays cached.</span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// The operator instruction goes after the history, invalidating nothing before it.</span>
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">messages</span> = append(<span style="color:#a6e22e">messages</span>, <span style="color:#a6e22e">userTurn</span>)
</span></span><span style="display:flex;"><span><span style="color:#a6e22e">messages</span> = append(<span style="color:#a6e22e">messages</span>, <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">MessageParam</span>{
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Role</span>: <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">MessageParamRoleSystem</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Content</span>: []<span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">ContentBlockParamUnion</span>{
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">anthropic</span>.<span style="color:#a6e22e">NewTextBlock</span>(<span style="color:#e6db74">&#34;Terse mode enabled — keep responses under 40 words.&#34;</span>),
</span></span><span style="display:flex;"><span>    },
</span></span><span style="display:flex;"><span>})
</span></span></code></pre></div><p>A message at turn five invalidates nothing before turn five. That is the whole trick.</p>
<p>Two constraints: it must follow a user message and be either the last entry or followed by an assistant turn — it cannot be <code>messages[0]</code>, so use the top-level <code>system</code> for the initial prompt. And support is model-dependent; unsupported models return a 400 saying the <code>system</code> role is not supported, so catch that and fall back to putting the instruction in a user turn.</p>
<h2 id="three-rules-that-beat-marker-placement">Three Rules That Beat Marker Placement</h2>
<p>Fix these before you fiddle with breakpoints.</p>
<p><strong>Freeze the system prompt.</strong> No dates, no user names, no modes. It is the front of the prefix and everything downstream depends on it not moving.</p>
<p><strong>Never change tools or model mid-conversation.</strong> Tools render at position zero, so adding, removing or reordering one invalidates the entire cache. Caches are also model-scoped, so switching models mid-conversation starts from cold. If you need &ldquo;modes&rdquo;, do not swap the tool set — pass the mode as message content.</p>
<p><strong>Forked calls must reuse the parent&rsquo;s exact prefix.</strong> Summarisation passes, sub-agents and side computations usually build their own request. If that fork rebuilds <code>system</code>, <code>tools</code> or <code>model</code> with any difference at all, it misses the parent&rsquo;s cache completely. Copy them verbatim and append the fork-specific content at the end.</p>
<p>That last one is also the argument against a &ldquo;cheap model for the easy stuff&rdquo; cascade, at least as a first move. Caches are per model, so routing between two models forfeits cache reuse across them. Measure the capable model at lower effort before you build the cascade — it is often cheaper <em>and</em> simpler, and it keeps one cache namespace.</p>
<h2 id="verifying-it-forever">Verifying It, Forever</h2>
<p>Every response carries the accounting (the same <code>Usage</code> struct I said to log from day one in <a href="/posts/calling-claude-from-go/">calling an LLM from Go</a>):</p>
<table>
	<thead>
			<tr>
					<th>Field</th>
					<th>Meaning</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>CacheCreationInputTokens</code></td>
					<td>Written to cache this request (you paid ~1.25×)</td>
			</tr>
			<tr>
					<td><code>CacheReadInputTokens</code></td>
					<td>Served from cache (you paid ~0.1×)</td>
			</tr>
			<tr>
					<td><code>InputTokens</code></td>
					<td>Full price, uncached</td>
			</tr>
	</tbody>
</table>
<p><code>InputTokens</code> is the <strong>uncached remainder only</strong> — not the prompt size. Total prompt = all three added together. If an agent ran for an hour and <code>InputTokens</code> reads 4K, the rest came from cache; check the sum, not the one field.</p>
<p>In a healthy multi-turn loop you should see, on each request:</p>
<ul>
<li><code>CacheReadInputTokens</code> — the whole prior prefix, growing turn over turn.</li>
<li><code>CacheCreationInputTokens</code> — roughly the last assistant turn plus the new input. Small.</li>
<li><code>InputTokens</code> — just the tail past the last breakpoint.</li>
</ul>
<p>If <code>CacheCreationInputTokens</code> is instead close to the full conversation size every time, the prefix is being rewritten upstream of your breakpoint. Go find the timestamp.</p>
<p><strong>And then keep checking.</strong> The expensive failure here is never the bad first implementation — it is the regression. Caching works the day you write it, then six weeks later somebody adds a dynamic field to the system prompt or a tool list that stopped being sorted, and every request misses. Nothing fails. Nothing pages. You find out from finance.</p>
<p>So make it a standing assertion, not a one-time look:</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">TestSystemPromptStaysCacheable</span>(<span style="color:#a6e22e">t</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">testing</span>.<span style="color:#a6e22e">T</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Two identical requests: the second must read from cache.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">first</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">callModel</span>(<span style="color:#a6e22e">t</span>, <span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">fixture</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">first</span>.<span style="color:#a6e22e">Usage</span>.<span style="color:#a6e22e">CacheCreationInputTokens</span> <span style="color:#f92672">==</span> <span style="color:#ae81ff">0</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">t</span>.<span style="color:#a6e22e">Fatalf</span>(<span style="color:#e6db74">&#34;nothing was cached: prompt may be under the model minimum&#34;</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">second</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">callModel</span>(<span style="color:#a6e22e">t</span>, <span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">fixture</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">second</span>.<span style="color:#a6e22e">Usage</span>.<span style="color:#a6e22e">CacheReadInputTokens</span> <span style="color:#f92672">==</span> <span style="color:#ae81ff">0</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">t</span>.<span style="color:#a6e22e">Errorf</span>(<span style="color:#e6db74">&#34;cache miss on an identical prompt — a silent invalidator crept in&#34;</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>That test costs a few cents to run and catches a regression that otherwise runs for months. Put it behind a build tag so it only runs when you mean it — the same treatment I gave integration tests in <a href="/posts/how-to-test-database-interactions-go/">how to test database interactions in Golang</a>. Better still, put a monitor on the ratio of <code>CacheReadInputTokens</code> to total input tokens and alert when it drops.</p>
<h2 id="checklist">Checklist</h2>
<ul>
<li>Frozen system prompt: no dates, IDs, names or conditional sections.</li>
<li>Deterministic tool list, sorted by name, identical across users.</li>
<li>One explicit breakpoint at the end of the static prefix; automatic caching for the tail.</li>
<li>Breakpoint at the end of the <em>shared</em> portion, not the end of the prompt.</li>
<li>5-minute TTL under continuous traffic; 1-hour only for 5–60 minute gaps.</li>
<li>Prompt above the model&rsquo;s minimum, which changes between models.</li>
<li>Dynamic context appended after the history, never templated into the system prompt.</li>
<li>Forks copy the parent&rsquo;s <code>system</code>, <code>tools</code> and <code>model</code> verbatim.</li>
<li>A test or monitor on the usage fields, checked on every change to prompt assembly.</li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<p>Prompt caching is the rare optimisation with no quality tradeoff: identical output, roughly a tenth of the input cost, and lower latency as a bonus. It is also unusually fragile, because it hinges on byte-exact prefixes and fails completely silently. Treat the prompt-building path the way you would treat a cache key anywhere else in your system — deterministic, stable, and covered by a test — and it mostly takes care of itself.</p>]]></content:encoded>
      <category>AI Engineering</category>
      <category>Performance Optimization</category>
      <category>Backend Development</category>
    </item>
    <item>
      <title>Why Use Golang: 9 Compelling Reasons for Your Next Project</title>
      <link>https://webdevstation.com/posts/why-use-golang/</link>
      <pubDate>Mon, 16 Jun 2025 15:21:00 +0200</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/why-use-golang/</guid>
      <description>Wondering why use Golang? Explore nine practical reasons — speed, concurrency, tooling and more — that make Go a top choice for modern back-end systems.</description>
      <content:encoded><![CDATA[<p>If you are evaluating programming languages for a new service or migrating an existing codebase, you have probably asked the question <strong><em>“why use Golang?”</em></strong>. In this article we will unpack the concrete, business-focused benefits of Go (often called <strong>Golang</strong>) and show when choosing Go is the right strategic move.</p>
<p>Go was created at Google to solve real-world problems—fast builds, simple deployment, and effortless concurrency—without sacrificing developer happiness. Let’s dive into nine reasons <strong>why you should use Golang</strong> in 2025 and beyond.</p>
<h2 id="quick-snapshot-benefits-at-a-glance">Quick Snapshot: Benefits at a Glance</h2>
<table>
	<thead>
			<tr>
					<th>Reason</th>
					<th>Developer Impact</th>
					<th>Business Impact</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>Native concurrency</td>
					<td>Easier parallel code, fewer race conditions</td>
					<td>Better CPU utilization, cost savings</td>
			</tr>
			<tr>
					<td>Fast compilation</td>
					<td>Iterate in seconds, not minutes</td>
					<td>Shorter release cycles</td>
			</tr>
			<tr>
					<td>Simple syntax</td>
					<td>Smaller learning curve</td>
					<td>Faster onboarding</td>
			</tr>
			<tr>
					<td>Robust stdlib</td>
					<td>Fewer external deps</td>
					<td>Reduced maintenance risk</td>
			</tr>
			<tr>
					<td>Single binary deploys</td>
					<td><code>scp</code> &amp; run—no runtime hassles</td>
					<td>Simplified CI/CD &amp; lower ops overhead</td>
			</tr>
			<tr>
					<td>First-class tooling</td>
					<td><code>go test</code>, <code>go vet</code>, <code>go fmt</code> built-in</td>
					<td>Higher code quality</td>
			</tr>
			<tr>
					<td>Memory safety</td>
					<td>Prevents many bugs upfront</td>
					<td>Increased uptime</td>
			</tr>
			<tr>
					<td>Growing ecosystem</td>
					<td>Mature frameworks, libs, &amp; CLIs</td>
					<td>Access to talent &amp; shared solutions</td>
			</tr>
			<tr>
					<td>Backed by giants</td>
					<td>Google, Cloudflare, Uber, etc.</td>
					<td>Long-term viability</td>
			</tr>
	</tbody>
</table>
<hr>
<h2 id="1-native-concurrency-model">1. Native Concurrency Model</h2>
<p>Go’s <strong>goroutines</strong> and <strong>channels</strong> deliver lightweight concurrency without complex thread management. Spawning 100 000 goroutines is commonplace:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">wg</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">sync</span>.<span style="color:#a6e22e">WaitGroup</span>{}
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span> &lt; <span style="color:#ae81ff">1e5</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Add</span>(<span style="color:#ae81ff">1</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">go</span> <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">id</span> <span style="color:#66d9ef">int</span>) {
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">defer</span> <span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Done</span>()
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#a6e22e">id</span>)
</span></span><span style="display:flex;"><span>        }(<span style="color:#a6e22e">i</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">wg</span>.<span style="color:#a6e22e">Wait</span>()
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Compare that with traditional threads and locks and the <em>why use Golang</em> answer becomes evident—<strong>parallelism is baked into the language</strong>.</p>
<h2 id="2-blazing-fast-compilation--execution">2. Blazing-Fast Compilation &amp; Execution</h2>
<p>Go produces native machine code and compiles large projects in <strong>seconds</strong>. Faster feedback loops boost productivity, and Go’s runtime performance rivals (and often exceeds) higher-level languages like Python or Node.js.</p>
<h2 id="3-pragmatic-readable-syntax">3. Pragmatic, Readable Syntax</h2>
<p>Go deliberately avoids generics-for-everything, implicit magic, and hidden control flow. The result is code that looks similar across companies, which means <strong>reading unfamiliar Go is easy</strong>.</p>
<h2 id="4-a-batteries-included-standard-library">4. A Batteries-Included Standard Library</h2>
<p>Need an HTTP server, JSON encoder, or RSA crypto? It’s already in <code>stdlib</code>. With fewer third-party dependencies, your supply-chain attack surface shrinks.</p>
<h2 id="5-static-binaries--effortless-deployment">5. Static Binaries &amp; Effortless Deployment</h2>
<p><code>go build</code> outputs a single, statically linked binary. Drop it in a container scratch image (~10 MB) or onto a bare server—no JVM, no interpreter.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>GOOS<span style="color:#f92672">=</span>linux GOARCH<span style="color:#f92672">=</span>amd64 go build -ldflags <span style="color:#e6db74">&#34;-s -w&#34;</span> -o app
</span></span></code></pre></div><h2 id="6-first-class-tooling-out-of-the-box">6. First-Class Tooling Out-of-the-Box</h2>
<p>Formatting (<code>go fmt</code>), linting (<code>go vet</code>), profiling (<code>pprof</code>), testing, and coverage are all standard. This uniform toolchain answers <strong>why use Golang</strong> for teams who value consistency.</p>
<h2 id="7-memory-safety--predictable-gc">7. Memory Safety &amp; Predictable GC</h2>
<p>Go’s garbage collector has reached <strong>&lt;1 ms 95th percentile pause times</strong> while retaining simplicity. With escape analysis and value semantics, many allocations disappear at compile time.</p>
<h2 id="8-vibrant-ecosystem--community">8. Vibrant Ecosystem &amp; Community</h2>
<p>Frameworks like <strong>Gin</strong>, <strong>Echo</strong>, and <strong>Fiber</strong> make web development painless; <strong>gRPC</strong> and <strong>Protocol Buffers</strong> have first-class support; and cloud providers ship Go SDKs on day one.</p>
<h2 id="9-proven-in-production-by-industry-leaders">9. Proven in Production by Industry Leaders</h2>
<p>Google (of course), Netflix, Uber, Cloudflare, Stripe, and many others run latency-critical systems in Go. That track record signals Go’s staying power.</p>
<hr>
<h2 id="when-not-to-use-go">When <em>Not</em> to Use Go</h2>
<p>While this article focuses on <strong>why to use Golang</strong>, balanced engineering requires knowing its limits:</p>
<ul>
<li>No generics for higher-kinded types (though basic generics landed in Go 1.18).</li>
<li>Runtime lacks a mature GUI library.</li>
<li>Manual error handling (<code>if err != nil</code>) can feel verbose.</li>
</ul>
<p>If your workload is <strong>numerical heavy-compute</strong> with tight SIMD requirements or requires sophisticated metaprogramming, Rust or C++ may fit better.</p>
<h2 id="conclusion">Conclusion</h2>
<p>So <strong>why use Golang</strong>? Because it delivers a rare combination of developer ergonomics, runtime efficiency, and operational simplicity. From microservices to CLI tools, Go empowers teams to ship reliable software—fast.</p>
<p>Ready to give Go a try? Install it, <code>go mod init</code>, and discover firsthand why thousands of engineers choose Golang every day.</p>
<p>If that convinced you, here is where I would start: <a href="/posts/understanding-golang-context/">understanding Golang context</a> for concurrency you can cancel, <a href="/posts/error-handling-in-go/">error handling in Go</a> for the idiom that trips up most newcomers, and <a href="/posts/one-of-thee-easiest-ways-to-host-go-web-apps/">one of the easiest ways to host your Go web app</a> for getting the result online. For what the language has picked up lately, see <a href="/posts/exciting-features-in-go-1-25/">exciting features coming in Go 1.25</a>.</p>]]></content:encoded>
      <category>Go Programming</category>
      <category>Backend Development</category>
    </item>
    <item>
      <title>Mastering Time in Go: From Basics to Best Practices</title>
      <link>https://webdevstation.com/posts/mastering-time-in-golang/</link>
      <pubDate>Mon, 09 Jun 2025 21:47:00 +0200</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/mastering-time-in-golang/</guid>
      <description>Explore how Go&#39;s time package provides powerful tools for handling dates, durations, and timers in your applications with clear examples and practical tips.</description>
      <content:encoded><![CDATA[<p>Time manipulation is a fundamental aspect of many applications, from logging and benchmarking to scheduling and timeout handling. Go&rsquo;s standard library includes a robust <code>time</code> package that provides elegant solutions for these common challenges. Let&rsquo;s explore how to effectively use Go&rsquo;s time primitives and avoid common pitfalls.</p>
<h2 id="understanding-time-in-go">Understanding Time in Go</h2>
<p>The Go language approaches time handling with its characteristic blend of simplicity and practicality. The <code>time</code> package centers around three key types:</p>
<ul>
<li><code>time.Time</code> - Represents a specific moment in time</li>
<li><code>time.Duration</code> - Represents the elapsed time between two points</li>
<li><code>time.Location</code> - Represents a time zone</li>
</ul>
<p>What makes Go&rsquo;s implementation particularly useful is its handling of monotonic time, which ensures consistent time measurements even when system clocks change.</p>
<h2 id="working-with-current-time">Working with Current Time</h2>
<p>Let&rsquo;s start with some basic operations:</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;time&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Get the current time</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">now</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Now</span>()
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Current time:&#34;</span>, <span style="color:#a6e22e">now</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Format the time using the reference time constant</span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Mon Jan 2 15:04:05 MST 2006</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Formatted time:&#34;</span>, <span style="color:#a6e22e">now</span>.<span style="color:#a6e22e">Format</span>(<span style="color:#e6db74">&#34;2006-01-02 15:04:05&#34;</span>))
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Get individual components</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;Year: %d, Month: %s, Day: %d\n&#34;</span>, 
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">now</span>.<span style="color:#a6e22e">Year</span>(), <span style="color:#a6e22e">now</span>.<span style="color:#a6e22e">Month</span>(), <span style="color:#a6e22e">now</span>.<span style="color:#a6e22e">Day</span>())
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Get Unix timestamp (seconds since January 1, 1970 UTC)</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Unix timestamp:&#34;</span>, <span style="color:#a6e22e">now</span>.<span style="color:#a6e22e">Unix</span>())
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>While most programming languages use arbitrary format strings like &ldquo;YYYY-MM-DD&rdquo;, Go uses a specific reference time: <strong>January 2, 2006 at 15:04:05</strong> (or 01/02 03:04:05PM &lsquo;06 -0700). This approach eliminates ambiguity and is easier to remember once you realize the pattern: 01/02 03:04:05PM &lsquo;06.</p>
<h2 id="time-parsing-and-time-zones">Time Parsing and Time Zones</h2>
<p>One of Go&rsquo;s strengths is its handling of time zones and time parsing. Let&rsquo;s see how to work with different formats and time zones:</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;time&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Parse a time string</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">timeStr</span> <span style="color:#f92672">:=</span> <span style="color:#e6db74">&#34;2025-06-09T21:47:00+02:00&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">t</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Parse</span>(<span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">RFC3339</span>, <span style="color:#a6e22e">timeStr</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">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Error parsing time:&#34;</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Parsed time:&#34;</span>, <span style="color:#a6e22e">t</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Convert to different timezone</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">utc</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">t</span>.<span style="color:#a6e22e">UTC</span>()
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;UTC time:&#34;</span>, <span style="color:#a6e22e">utc</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Load a specific location</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">loc</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">LoadLocation</span>(<span style="color:#e6db74">&#34;America/New_York&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Error loading location:&#34;</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Convert time to the new location</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">nyTime</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">t</span>.<span style="color:#a6e22e">In</span>(<span style="color:#a6e22e">loc</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;New York time:&#34;</span>, <span style="color:#a6e22e">nyTime</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>A common source of bugs is assuming all times are UTC. In a distributed system, always be explicit about time zones when communicating timestamps between services.</p>
<h2 id="measuring-time-and-performance">Measuring Time and Performance</h2>
<p>Go&rsquo;s <code>time</code> package shines when measuring code performance:</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;time&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">someExpensiveOperation</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Simulate work</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Sleep</span>(<span style="color:#ae81ff">100</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Millisecond</span>)
</span></span><span style="display:flex;"><span>}
</span></span><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">start</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Now</span>()
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">someExpensiveOperation</span>()
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">elapsed</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Since</span>(<span style="color:#a6e22e">start</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;Operation took %s\n&#34;</span>, <span style="color:#a6e22e">elapsed</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Alternatively with the same result</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;Operation took %s\n&#34;</span>, <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Now</span>().<span style="color:#a6e22e">Sub</span>(<span style="color:#a6e22e">start</span>))
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>The <code>time.Since()</code> function is a convenient shorthand for <code>time.Now().Sub(start)</code>, making benchmarking code cleaner.</p>
<h2 id="working-with-durations">Working with Durations</h2>
<p>The <code>time.Duration</code> type is a nanosecond-precision interval that offers intuitive operations:</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;time&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Create durations using typed constants</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">second</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">1</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">minute</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">1</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Minute</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">hour</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">1</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Hour</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Arithmetic with durations</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">total</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">second</span> <span style="color:#f92672">+</span> <span style="color:#ae81ff">30</span><span style="color:#f92672">*</span><span style="color:#a6e22e">minute</span> <span style="color:#f92672">+</span> <span style="color:#ae81ff">2</span><span style="color:#f92672">*</span><span style="color:#a6e22e">hour</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;Total duration: %v (%s)\n&#34;</span>, <span style="color:#a6e22e">total</span>, <span style="color:#a6e22e">total</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Parsing durations from strings</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">d</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">ParseDuration</span>(<span style="color:#e6db74">&#34;1h30m15s&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Error parsing duration:&#34;</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">return</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;Parsed duration: %v\n&#34;</span>, <span style="color:#a6e22e">d</span>)
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Converting durations</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;In seconds: %.2f\n&#34;</span>, <span style="color:#a6e22e">d</span>.<span style="color:#a6e22e">Seconds</span>())
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;In minutes: %.2f\n&#34;</span>, <span style="color:#a6e22e">d</span>.<span style="color:#a6e22e">Minutes</span>())
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;In hours: %.2f\n&#34;</span>, <span style="color:#a6e22e">d</span>.<span style="color:#a6e22e">Hours</span>())
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Note that <code>time.Duration</code> has a maximum range of approximately 290 years. For longer time periods, you&rsquo;ll need to use <code>time.Time</code> instead.</p>
<h2 id="timers-and-tickers">Timers and Tickers</h2>
<p>For scheduling operations, Go provides timers and tickers:</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;time&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Create a timer that will fire once after 2 seconds</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">timer</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">NewTimer</span>(<span style="color:#ae81ff">2</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Timer started at&#34;</span>, <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Now</span>().<span style="color:#a6e22e">Format</span>(<span style="color:#e6db74">&#34;15:04:05&#34;</span>))
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Wait for the timer to fire</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">&lt;-</span><span style="color:#a6e22e">timer</span>.<span style="color:#a6e22e">C</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Timer fired at&#34;</span>, <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Now</span>().<span style="color:#a6e22e">Format</span>(<span style="color:#e6db74">&#34;15:04:05&#34;</span>))
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Create a ticker that fires every second</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">ticker</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">NewTicker</span>(<span style="color:#ae81ff">1</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">counter</span> <span style="color:#f92672">:=</span> <span style="color:#ae81ff">0</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Process ticker events for 5 seconds</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;-</span><span style="color:#a6e22e">ticker</span>.<span style="color:#a6e22e">C</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">counter</span><span style="color:#f92672">++</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Tick&#34;</span>, <span style="color:#a6e22e">counter</span>, <span style="color:#e6db74">&#34;at&#34;</span>, <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Now</span>().<span style="color:#a6e22e">Format</span>(<span style="color:#e6db74">&#34;15:04:05&#34;</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">counter</span> <span style="color:#f92672">&gt;=</span> <span style="color:#ae81ff">5</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">ticker</span>.<span style="color:#a6e22e">Stop</span>()
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">break</span>
</span></span><span style="display:flex;"><span>        }
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Ticker stopped&#34;</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>A common mistake is forgetting to stop tickers when they&rsquo;re no longer needed, which can lead to goroutine leaks.</p>
<h2 id="performance-tips-and-best-practices">Performance Tips and Best Practices</h2>
<p>After working with Go&rsquo;s time package across multiple production systems, I&rsquo;ve identified several best practices:</p>
<ol>
<li>
<p><strong>Avoid frequent calls to <code>time.Now()</code></strong> in tight loops, as it involves a system call.</p>
</li>
<li>
<p><strong>Use monotonic time for duration measurements</strong>. Go handles this automatically when you call <code>time.Now()</code>, but be aware that serializing and deserializing a <code>time.Time</code> loses the monotonic component.</p>
</li>
<li>
<p><strong>Be consistent with time zones</strong>, especially when storing times in databases. Either standardize on UTC for storage or be explicit about the time zone.</p>
</li>
<li>
<p><strong>For repeated timer operations</strong>, use <code>time.Ticker</code> instead of repeatedly creating new timers.</p>
</li>
<li>
<p><strong>When parsing user input</strong>, prefer to use explicit formats with <code>time.Parse</code> rather than magic formatting functions.</p>
</li>
</ol>
<p>Here&rsquo;s an example of a common anti-pattern and how to fix it:</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">// Anti-pattern - creating many timers</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">antiPattern</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">1000</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">timer</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">NewTimer</span>(<span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;-</span><span style="color:#a6e22e">timer</span>.<span style="color:#a6e22e">C</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">doSomething</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">// Better approach - reuse a single timer</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">betterApproach</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">timer</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">NewTimer</span>(<span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</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">1000</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#f92672">&lt;-</span><span style="color:#a6e22e">timer</span>.<span style="color:#a6e22e">C</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">doSomething</span>()
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">timer</span>.<span style="color:#a6e22e">Reset</span>(<span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Second</span>)
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">timer</span>.<span style="color:#a6e22e">Stop</span>()
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h2 id="conclusion">Conclusion</h2>
<p>Go&rsquo;s <code>time</code> package provides an intuitive API for handling all aspects of time in your applications. From simple date formatting to sophisticated time measurements, it offers a consistent approach that aligns with Go&rsquo;s philosophy of simplicity and practicality.</p>
<p>By understanding how Go approaches time handling, you can write more efficient and reliable code, avoid common pitfalls, and leverage the full power of Go&rsquo;s time primitives.</p>
<p><em>See also: <a href="/posts/understanding-golang-context/">understanding Golang context</a> for deadlines and timeouts, and <a href="/posts/mastering-for-loops-in-go/">mastering Golang for loops</a> for the iteration patterns these examples lean on.</em></p>
<hr>
<p><em>How do you handle time operations in your Go applications? Have you encountered any challenging time-related bugs? Share your experiences in the comments below!</em></p>]]></content:encoded>
      <category>Go Programming</category>
      <category>Backend Development</category>
    </item>
    <item>
      <title>Exciting Features Coming in Go 1.25: What to Expect</title>
      <link>https://webdevstation.com/posts/exciting-features-in-go-1-25/</link>
      <pubDate>Mon, 19 May 2025 08:57:00 +0200</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/exciting-features-in-go-1-25/</guid>
      <description>Explore the key improvements in Go 1.25, including stable profile-guided optimization, enhanced compiler performance, and new standard library additions for more…</description>
      <content:encoded><![CDATA[<p>Go 1.25 is on the horizon, and it&rsquo;s bringing some exciting improvements to the language. Let&rsquo;s explore the confirmed features that will make Go development even more productive and efficient.</p>
<h2 id="profile-guided-optimization">Profile-guided optimization</h2>
<p>One of the most significant additions in Go 1.25 is the stabilization of profile-guided optimization (PGO). After being introduced as an experimental feature in Go 1.20 and improved in subsequent releases, PGO is finally becoming a stable feature in Go 1.25.</p>
<p>Profile-guided optimization allows the compiler to optimize code based on runtime behavior profiles. By analyzing how your application actually runs in production, the compiler can make more intelligent optimization decisions.</p>
<p>Here&rsquo;s how you can use it:</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">// First, run your application with profiling enabled</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">go</span> <span style="color:#a6e22e">build</span> <span style="color:#f92672">-</span><span style="color:#a6e22e">pgo</span>=<span style="color:#a6e22e">off</span> <span style="color:#a6e22e">myapp</span>.<span style="color:#66d9ef">go</span>
</span></span><span style="display:flex;"><span>.<span style="color:#f92672">/</span><span style="color:#a6e22e">myapp</span> <span style="color:#f92672">-</span><span style="color:#a6e22e">cpuprofile</span>=<span style="color:#a6e22e">profile</span>.<span style="color:#a6e22e">pprof</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Then compile with PGO</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">go</span> <span style="color:#a6e22e">build</span> <span style="color:#f92672">-</span><span style="color:#a6e22e">pgo</span>=<span style="color:#a6e22e">profile</span>.<span style="color:#a6e22e">pprof</span> <span style="color:#a6e22e">myapp</span>.<span style="color:#66d9ef">go</span>
</span></span></code></pre></div><p>The benefits are substantial - benchmarks have shown performance improvements of 2-7% for real-world applications, with some hot paths seeing even greater gains.</p>
<h2 id="improved-garbage-collection">Improved Garbage Collection</h2>
<p>Go 1.25 continues the work on improving the garbage collector&rsquo;s performance. The new version brings lower latency and better memory management, particularly for applications with large heaps.</p>
<p>The improvements focus on reducing GC pause times and making them more predictable, which is crucial for applications requiring consistent performance.</p>
<h2 id="enhanced-toolchain-security">Enhanced Toolchain Security</h2>
<p>Security gets a boost in Go 1.25 with improvements to the toolchain. The <code>go</code> command now includes better verification of module authenticity and additional checks to prevent supply chain attacks.</p>
<h2 id="better-error-handling">Better Error Handling</h2>
<p>Error handling in Go has been incrementally improving, and Go 1.25 continues this trend with enhancements to the <code>errors</code> package. The improvements make it easier to wrap, unwrap, and inspect errors in a more structured way.</p>
<p>One of the key improvements is the addition of new functions to the <code>errors</code> package that provide more flexibility when working with error chains. Let&rsquo;s look at some examples of how these new features can be used.</p>
<h3 id="enhanced-error-wrapping">Enhanced Error Wrapping</h3>
<p>Go 1.25 introduces a more powerful way to wrap errors with additional context while preserving the original error information:</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;errors&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;fmt&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></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">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">processFile</span>(<span style="color:#e6db74">&#34;config.json&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// Output: failed to process config.json: file not found</span>
</span></span><span style="display:flex;"><span>        
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// Check if it&#39;s a specific error type</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">errors</span>.<span style="color:#a6e22e">Is</span>(<span style="color:#a6e22e">err</span>, <span style="color:#a6e22e">ErrNotFound</span>) {
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;The file was not found, please check the path&#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">// Get additional context from the error</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">fileErr</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">FileError</span>
</span></span><span style="display:flex;"><span>        <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">errors</span>.<span style="color:#a6e22e">As</span>(<span style="color:#a6e22e">err</span>, <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">fileErr</span>) {
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;Error occurred with file: %s\n&#34;</span>, <span style="color:#a6e22e">fileErr</span>.<span style="color:#a6e22e">Filename</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:#75715e">// ErrNotFound is a sentinel error</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">ErrNotFound</span> = <span style="color:#a6e22e">errors</span>.<span style="color:#a6e22e">New</span>(<span style="color:#e6db74">&#34;file not found&#34;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// FileError is a custom error type with additional context</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">FileError</span> <span style="color:#66d9ef">struct</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">Filename</span> <span style="color:#66d9ef">string</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">e</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">FileError</span>) <span style="color:#a6e22e">Error</span>() <span style="color:#66d9ef">string</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Sprintf</span>(<span style="color:#e6db74">&#34;failed to process %s: %v&#34;</span>, <span style="color:#a6e22e">e</span>.<span style="color:#a6e22e">Filename</span>, <span style="color:#a6e22e">e</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">func</span> (<span style="color:#a6e22e">e</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">FileError</span>) <span style="color:#a6e22e">Unwrap</span>() <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">e</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">func</span> <span style="color:#a6e22e">processFile</span>(<span style="color:#a6e22e">filename</span> <span style="color:#66d9ef">string</span>) <span style="color:#66d9ef">error</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Simulate a file not found error</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">FileError</span>{
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">Filename</span>: <span style="color:#a6e22e">filename</span>,
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">Err</span>:      <span style="color:#a6e22e">ErrNotFound</span>,
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><h3 id="new-error-grouping">New Error Grouping</h3>
<p>Go 1.25 introduces a new way to handle multiple errors together, which is particularly useful for concurrent operations:</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;errors&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;fmt&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;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">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Process multiple files concurrently</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">filenames</span> <span style="color:#f92672">:=</span> []<span style="color:#66d9ef">string</span>{<span style="color:#e6db74">&#34;config.json&#34;</span>, <span style="color:#e6db74">&#34;data.csv&#34;</span>, <span style="color:#e6db74">&#34;settings.yaml&#34;</span>}
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Create a new error group</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">errGroup</span> <span style="color:#a6e22e">errors</span>.<span style="color:#a6e22e">ErrorGroup</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Use a wait group to wait for all goroutines</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></span><span style="display:flex;"><span>    <span style="color:#66d9ef">for</span> <span style="color:#a6e22e">_</span>, <span style="color:#a6e22e">filename</span> <span style="color:#f92672">:=</span> <span style="color:#66d9ef">range</span> <span style="color:#a6e22e">filenames</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></span><span style="display:flex;"><span>        <span style="color:#66d9ef">go</span> <span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">filename</span> <span style="color:#66d9ef">string</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></span><span style="display:flex;"><span>            <span style="color:#75715e">// Process the file</span>
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">processFile</span>(<span style="color:#a6e22e">filename</span>)
</span></span><span style="display:flex;"><span>            <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>                <span style="color:#75715e">// Add the error to the group</span>
</span></span><span style="display:flex;"><span>                <span style="color:#a6e22e">errGroup</span>.<span style="color:#a6e22e">Add</span>(<span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>            }
</span></span><span style="display:flex;"><span>        }(<span style="color:#a6e22e">filename</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">// Wait for all goroutines to complete</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><span style="display:flex;"><span>    <span style="color:#75715e">// Check if any errors occurred</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">errGroup</span>.<span style="color:#a6e22e">Err</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">fmt</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Errors occurred during processing:&#34;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Println</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">// We can also iterate through individual errors</span>
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">errGroup</span>.<span style="color:#a6e22e">Range</span>(<span style="color:#66d9ef">func</span>(<span style="color:#a6e22e">err</span> <span style="color:#66d9ef">error</span>) <span style="color:#66d9ef">bool</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;- %v\n&#34;</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 style="color:#75715e">// continue iteration</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><h3 id="improved-error-formatting">Improved Error Formatting</h3>
<p>Go 1.25 also improves how errors are formatted, making it easier to get meaningful information when debugging:</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;errors&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#e6db74">&#34;fmt&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></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">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">deepFunction</span>()
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Print the error with detailed formatting</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;%+v\n&#34;</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Output includes the error message and stack trace</span>
</span></span><span style="display:flex;"><span>    
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Get a simplified view</span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Printf</span>(<span style="color:#e6db74">&#34;%v\n&#34;</span>, <span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Output: level3: level2: level1: base 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">deepFunction</span>() <span style="color:#66d9ef">error</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;level3: %w&#34;</span>, <span style="color:#a6e22e">middleFunction</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">middleFunction</span>() <span style="color:#66d9ef">error</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;level2: %w&#34;</span>, <span style="color:#a6e22e">baseFunction</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">baseFunction</span>() <span style="color:#66d9ef">error</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;level1: %w&#34;</span>, <span style="color:#a6e22e">errors</span>.<span style="color:#a6e22e">New</span>(<span style="color:#e6db74">&#34;base error&#34;</span>))
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>These improvements make error handling in Go more robust and expressive, while still maintaining the simplicity and explicitness that Go is known for.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Go 1.25 represents another solid step forward for the language, focusing on performance, security, and developer experience. The stable release of profile-guided optimization is particularly exciting, as it allows for more intelligent performance optimizations based on real-world usage patterns.</p>
<p>As always, Go maintains its commitment to backward compatibility, so you can upgrade with confidence knowing your existing code will continue to work.</p>
<p>Stay tuned for the official release, and in the meantime, you can try out the beta versions to get a head start on these exciting new features.</p>
<p><em>Update: two releases on, Go 1.27 turned out to be the bigger deal — it adds generic methods to the language and lands JSON v2 and a <code>uuid</code> package in the standard library. See <a href="/posts/whats-new-in-go-1-27/">what&rsquo;s new in Go 1.27</a>.</em></p>
<p>If you are still weighing Go up rather than upgrading it, <a href="/posts/why-use-golang/">why use Golang</a> makes the wider case. And two additions from recent releases that changed how I write everyday code: <a href="/posts/structured-logging-in-go-with-slog/">structured logging with log/slog</a> and the per-iteration loop variables covered in <a href="/posts/mastering-for-loops-in-go/">mastering Golang for loops</a>.</p>]]></content:encoded>
      <category>Go Programming</category>
      <category>Backend Development</category>
    </item>
    <item>
      <title>103 Early Hints in Go, or the new Way of How to Improve Performance of a Web Page written in Go</title>
      <link>https://webdevstation.com/posts/early-hints-in-go-or-the-way-of-how-to-improve-perforamnce-of-web-page/</link>
      <pubDate>Mon, 14 Nov 2022 19:40:21 +0000</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/early-hints-in-go-or-the-way-of-how-to-improve-perforamnce-of-web-page/</guid>
      <description>Learn how to implement HTTP 103 Early Hints in Go 1.19+ to significantly improve web page loading performance by enabling browsers to preload resources while waiting…</description>
      <content:encoded><![CDATA[<p>Since Go 1.19 we can use a new <code>103 (Early Hints)</code> http status code when we create web applications. Let&rsquo;s figure out how and when this could help us.
We are going to create a simple golang web server that servers some html content. One html page will be served with <code>103</code> header and another one without.
After loading comparison we will see how early hints can improve page performance.</p>
<p>Early hints is a special HTTP header that is sent before the web server sends the final HTTP response to the client. At this moment it&rsquo;s supported only by Chrome browser.
As soon as the browser requests a page, server immediately returns 103 early hints header. In the meantime, a server will generate a usual HTTP response. This helps us utilize in maximum the loading time by letting browser know what resources it should preload while waiting for the final response from a server.</p>
<p>Enough theory, let&rsquo;s write some code :)</p>
<p>First, I&rsquo;m going to create an index.html with some dummy structure. Also, I will load <code>bootsrap</code> frontend framework to simulate some heavy css and js references during page load.</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-html" data-lang="html"><span style="display:flex;"><span>&lt;<span style="color:#f92672">html</span>&gt;
</span></span><span style="display:flex;"><span>  &lt;<span style="color:#f92672">head</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">title</span>&gt;Hello!&lt;/<span style="color:#f92672">title</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">link</span> <span style="color:#a6e22e">href</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css&#34;</span> <span style="color:#a6e22e">rel</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;stylesheet&#34;</span> <span style="color:#a6e22e">integrity</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;sha384-Zenh87qX5JnK2Jl0vWa8Ck2rdkQ2Bzep5IDxbcnCeuOxjzrPF/et3URy9Bv1WTRi&#34;</span> <span style="color:#a6e22e">crossorigin</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;anonymous&#34;</span>&gt;
</span></span><span style="display:flex;"><span>  &lt;/<span style="color:#f92672">head</span>&gt;
</span></span><span style="display:flex;"><span>  &lt;<span style="color:#f92672">body</span>&gt;
</span></span><span style="display:flex;"><span>  &lt;<span style="color:#f92672">div</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;p-2 bg-success&#34;</span>&gt;
</span></span><span style="display:flex;"><span>    &lt;<span style="color:#f92672">h1</span> <span style="color:#a6e22e">class</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;text-white&#34;</span>&gt;Hello!&lt;/<span style="color:#f92672">h1</span>&gt;
</span></span><span style="display:flex;"><span>  &lt;/<span style="color:#f92672">div</span>&gt;
</span></span><span style="display:flex;"><span>  &lt;<span style="color:#f92672">script</span> <span style="color:#a6e22e">src</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/js/bootstrap.bundle.min.js&#34;</span> <span style="color:#a6e22e">integrity</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;sha384-OERcA2EqjJCMA+/3y+gxIOqMEjwtxJY7qPCqsdltbNJuaOe923+mo//f6V8Qbsw3&#34;</span> <span style="color:#a6e22e">crossorigin</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;anonymous&#34;</span>&gt;&lt;/<span style="color:#f92672">script</span>&gt;
</span></span><span style="display:flex;"><span>  &lt;/<span style="color:#f92672">body</span>&gt;
</span></span><span style="display:flex;"><span>&lt;/<span style="color:#f92672">html</span>&gt;
</span></span></code></pre></div><p>Now we need to serve it. Let&rsquo;s create a server.</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:#75715e">//go:embed index.html</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">var</span> <span style="color:#a6e22e">index</span> <span style="color:#66d9ef">string</span> <span style="color:#75715e">// embeded index.html</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">log</span>.<span style="color:#a6e22e">Println</span>(<span style="color:#e6db74">&#34;Starting server...&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Handler for the page without early hints.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">HandleFunc</span>(<span style="color:#e6db74">&#34;/1&#34;</span>, <span style="color:#a6e22e">noHintsHandler</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Handler for the page with early hints</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">HandleFunc</span>(<span style="color:#e6db74">&#34;/2&#34;</span>, <span style="color:#a6e22e">withHintsHandler</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">http</span>.<span style="color:#a6e22e">ListenAndServe</span>(<span style="color:#e6db74">&#34;:8082&#34;</span>, <span style="color:#66d9ef">nil</span>); <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">log</span>.<span style="color:#a6e22e">Fatal</span>(<span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">noHintsHandler</span>(<span style="color:#a6e22e">w</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ResponseWriter</span>, <span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Request</span>) {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">t</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">template</span>.<span style="color:#a6e22e">New</span>(<span style="color:#e6db74">&#34;&#34;</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">t</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">t</span>.<span style="color:#a6e22e">Parse</span>(<span style="color:#a6e22e">index</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">Println</span>(<span style="color:#a6e22e">err</span>)
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">t</span>.<span style="color:#a6e22e">Execute</span>(<span style="color:#a6e22e">w</span>, <span style="color:#66d9ef">nil</span>)
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">withHintsHandler</span>(<span style="color:#a6e22e">w</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">ResponseWriter</span>, <span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">Request</span>) {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Adding headers with preload information for bootstrap.min.css and bootstrap.bundle.min.js</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">Header</span>().<span style="color:#a6e22e">Add</span>(<span style="color:#e6db74">&#34;Link&#34;</span>, <span style="color:#e6db74">&#34;&lt;https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css&gt;; rel=preload; as=style&#34;</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">Header</span>().<span style="color:#a6e22e">Add</span>(<span style="color:#e6db74">&#34;Link&#34;</span>, <span style="color:#e6db74">&#34;&lt;https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/js/bootstrap.bundle.min.js&gt;; rel=preload; as=script&#34;</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Sending 103 status code.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">WriteHeader</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusEarlyHints</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">t</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">template</span>.<span style="color:#a6e22e">New</span>(<span style="color:#e6db74">&#34;&#34;</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">t</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">t</span>.<span style="color:#a6e22e">Parse</span>(<span style="color:#a6e22e">index</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">Println</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">// Sending 200 status code.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">w</span>.<span style="color:#a6e22e">WriteHeader</span>(<span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">StatusOK</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">t</span>.<span style="color:#a6e22e">Execute</span>(<span style="color:#a6e22e">w</span>, <span style="color:#66d9ef">nil</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Now it&rsquo;s time to see our pages in action! Run our server <code>go run main.go</code>, open the page without early hints <code>http://localhost:8082/1</code> in Chrome,
open inspector, go to Lighthouse tab and click on &ldquo;Analyze page load&rdquo; button.
And this is what we can see:
<img src="/images/2022/1.png" alt="Chrome Lighthouse report for the Go page without early hints, showing a First Contentful Paint of 1492.8ms" title="Performance results for the page without early hints">
It takes a while until bootstrap resources got loaded by a browser. As result, FCP (First Contentful Paint) is <code>1492,8ms</code>.</p>
<p>Now, let&rsquo;s do the same for the page with the early hints <code>http://localhost:8082/2</code> And this is a result:
<img src="/images/2022/2.png" alt="Chrome Lighthouse report for the same page served with 103 Early Hints, showing a First Contentful Paint of 437.8ms" title="Performance results for the page with early hints">
As you can see, the page loaded much faster now. Bootstrap dependencies (bootstrap.min.css and bootstrap.bundle.min.js) were preloaded in the beginning and FCP now is <code>437,8ms</code>. More than 3 times faster, quite an impressive result!</p>
<p>However, it does not mean that you have to preload absolutely all resources now. Just try to experiment with these things, see how it affects your page performance and decide for yourself the right balance.</p>
<p>You can find the source code <a href="https://github.com/alexsergivan/blog-examples/tree/master/early-hints">here</a>.</p>
<p>If you want to measure the difference on your own service rather than take my numbers for it, <a href="/posts/an-easy-way-to-loadtest-your-web-apps/">an easy way to load test your web apps</a> shows the setup I use. And for the wins that happen before the request even reaches your handler, have a look at <a href="/posts/how-to-make-nginx-cookie-aware/">how to make Nginx cache cookie aware</a>.</p>]]></content:encoded>
      <category>Performance Optimization</category>
      <category>Web Development</category>
    </item>
    <item>
      <title>Example of how Golang generics minimize the amount of code you need to write</title>
      <link>https://webdevstation.com/posts/example-of-how-generics-simplify-golang/</link>
      <pubDate>Thu, 09 Jun 2022 15:41:52 +0000</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/example-of-how-generics-simplify-golang/</guid>
      <description>Explore practical examples of Go 1.18 generics in action by refactoring caching logic in a real-world application to write cleaner, more maintainable code with less…</description>
      <content:encoded><![CDATA[<p>I guess that almost everyone in the go community was exciting when Go 1.18 was released, especially because of generics.
Some days ago I decided to try generics in the real-world application, by refactoring some of its pieces, related to a caching logic.</p>
<p>In our web application, we have multiple resolvers that execute some sql queries and return data in different types. Obviously,
we want to prevent the database overloading by caching the same responses.</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:#75715e">// MyExampleType1 type to serve example response 1.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">type</span> <span style="color:#a6e22e">MyExampleType1</span> <span style="color:#66d9ef">struct</span> {}
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// MyExampleType2 type to serve example response 2.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">type</span> <span style="color:#a6e22e">MyExampleType2</span> <span style="color:#66d9ef">struct</span> {}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// MyCachedResolver1 checks if there are any cached data by specific key.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// If nothing in cache, queries the database and adds result to the redis cache.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">func</span> <span style="color:#a6e22e">MyCachedResolver1</span>(<span style="color:#a6e22e">redisClient</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">redis</span>.<span style="color:#a6e22e">Client</span>) ([]<span style="color:#a6e22e">MyExampleType1</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">key</span> <span style="color:#f92672">:=</span> <span style="color:#e6db74">&#34;resolver_result&#34;</span>
</span></span><span style="display:flex;"><span>	  <span style="color:#a6e22e">value</span>, <span style="color:#a6e22e">found</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">redisClient</span>.<span style="color:#a6e22e">Get</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">key</span>)
</span></span><span style="display:flex;"><span>	  <span style="color:#66d9ef">if</span> !<span style="color:#a6e22e">found</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">value</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">FetchSomethingHeavyFromDB</span>()
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>, <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">valueJson</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">json</span>.<span style="color:#a6e22e">Marshal</span>(<span style="color:#a6e22e">value</span>)
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>, <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">redisClient</span>.<span style="color:#a6e22e">Set</span>(<span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Background</span>(), <span style="color:#a6e22e">key</span>, <span style="color:#a6e22e">valueJson</span>, <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Hour</span><span style="color:#f92672">*</span><span style="color:#ae81ff">12</span>)
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">value</span>, <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>	  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	  <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">val</span> []<span style="color:#a6e22e">MyExampleType1</span>
</span></span><span style="display:flex;"><span>	  <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">json</span>.<span style="color:#a6e22e">Unmarshal</span>([]byte(<span style="color:#a6e22e">value</span>.(<span style="color:#66d9ef">string</span>)), <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">val</span>)
</span></span><span style="display:flex;"><span>	  <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>, <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>	  }
</span></span><span style="display:flex;"><span>	  <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">val</span>, <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>    }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// MyCachedResolver2 checks if there are any cached data by specific key.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// If nothing in cache, queries the database and adds result to the redis cache.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">func</span> <span style="color:#a6e22e">MyCachedResolver2</span>(<span style="color:#a6e22e">redisClient</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">redis</span>.<span style="color:#a6e22e">Client</span>) ([]<span style="color:#a6e22e">MyExampleType2</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>      <span style="color:#a6e22e">key</span> <span style="color:#f92672">:=</span> <span style="color:#e6db74">&#34;resolver_result_2&#34;</span>
</span></span><span style="display:flex;"><span>	  <span style="color:#a6e22e">value</span>, <span style="color:#a6e22e">found</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">redisClient</span>.<span style="color:#a6e22e">Get</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">key</span>)
</span></span><span style="display:flex;"><span>	  <span style="color:#66d9ef">if</span> !<span style="color:#a6e22e">found</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">value</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">FetchSomethingEvenMoreHeavierFromDB</span>()
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>, <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">valueJson</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">json</span>.<span style="color:#a6e22e">Marshal</span>(<span style="color:#a6e22e">value</span>)
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>, <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">redisClient</span>.<span style="color:#a6e22e">Set</span>(<span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Background</span>(), <span style="color:#a6e22e">key</span>, <span style="color:#a6e22e">valueJson</span>, <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Hour</span><span style="color:#f92672">*</span><span style="color:#ae81ff">12</span>)
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">value</span>, <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>	  }
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	  <span style="color:#66d9ef">var</span> <span style="color:#a6e22e">val</span> []<span style="color:#a6e22e">MyExampleType2</span>
</span></span><span style="display:flex;"><span>	  <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">json</span>.<span style="color:#a6e22e">Unmarshal</span>([]byte(<span style="color:#a6e22e">value</span>.(<span style="color:#66d9ef">string</span>)), <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">val</span>)
</span></span><span style="display:flex;"><span>	  <span style="color:#66d9ef">if</span> <span style="color:#a6e22e">err</span> <span style="color:#f92672">!=</span> <span style="color:#66d9ef">nil</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> <span style="color:#66d9ef">nil</span>, <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>	  }
</span></span><span style="display:flex;"><span>	  <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">val</span>, <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>    }
</span></span></code></pre></div><p>As you can see, there is some repetitive code that should be written due to the different types <code>MyExampleType1</code> and <code>MyExampleType2</code>.</p>
<p>Now, let&rsquo;s see how we can improve this situation by using generics. I&rsquo;m going to write a function <code>WithCache()</code> which will be responsible for
setting and getting data to/from redis cache.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span>    <span style="color:#75715e">// WithCache adds exec function result into the redis cache.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Pay attention to &#34;T any&#34; that allows us to pass any type to this function.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">func</span> <span style="color:#a6e22e">WithCache</span>[<span style="color:#a6e22e">T</span> <span style="color:#66d9ef">any</span>](<span style="color:#a6e22e">redisClient</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">redis</span>.<span style="color:#a6e22e">Client</span>, <span style="color:#a6e22e">ctx</span> <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Context</span>, <span style="color:#a6e22e">key</span> <span style="color:#66d9ef">string</span>, <span style="color:#a6e22e">exec</span> <span style="color:#66d9ef">func</span>() (<span style="color:#a6e22e">T</span>, <span style="color:#66d9ef">error</span>), <span style="color:#a6e22e">ttl</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Duration</span>) (<span style="color:#a6e22e">T</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>    	<span style="color:#a6e22e">value</span>, <span style="color:#a6e22e">found</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">redisClient</span>.<span style="color:#a6e22e">Get</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">key</span>)
</span></span><span style="display:flex;"><span>    	<span style="color:#66d9ef">var</span> <span style="color:#a6e22e">result</span> <span style="color:#a6e22e">T</span>
</span></span><span style="display:flex;"><span>    	<span style="color:#66d9ef">if</span> !<span style="color:#a6e22e">found</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#75715e">// exec() is a function that should be executed to fetch needed data.</span>
</span></span><span style="display:flex;"><span>    		<span style="color:#a6e22e">value</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">exec</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">result</span>, <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>    		}
</span></span><span style="display:flex;"><span>    		<span style="color:#a6e22e">jsonValue</span>, <span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">json</span>.<span style="color:#a6e22e">Marshal</span>(<span style="color:#a6e22e">value</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">result</span>, <span style="color:#a6e22e">err</span>
</span></span><span style="display:flex;"><span>    		}
</span></span><span style="display:flex;"><span>    		<span style="color:#a6e22e">redisClient</span>.<span style="color:#a6e22e">Set</span>(<span style="color:#a6e22e">ctx</span>, <span style="color:#a6e22e">key</span>, <span style="color:#a6e22e">jsonValue</span>, <span style="color:#a6e22e">ttl</span>)
</span></span><span style="display:flex;"><span>    		<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">value</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">var</span> <span style="color:#a6e22e">val</span> <span style="color:#a6e22e">T</span>
</span></span><span style="display:flex;"><span>    	<span style="color:#a6e22e">err</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">json</span>.<span style="color:#a6e22e">Unmarshal</span>([]byte(<span style="color:#a6e22e">value</span>.(<span style="color:#66d9ef">string</span>)), <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">val</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">result</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">val</span>, <span style="color:#66d9ef">nil</span>
</span></span><span style="display:flex;"><span>    }
</span></span></code></pre></div><p>After that our resolvers could be refactored into this:</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">MyCachedResolver1</span>(<span style="color:#a6e22e">redisClient</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">redis</span>.<span style="color:#a6e22e">Client</span>) ([]<span style="color:#a6e22e">MyExampleType1</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>       <span style="color:#a6e22e">key</span> <span style="color:#f92672">:=</span> <span style="color:#e6db74">&#34;resolver_result_1&#34;</span>
</span></span><span style="display:flex;"><span>       <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">WithCache</span>[[]<span style="color:#a6e22e">MyExampleType1</span>](<span style="color:#a6e22e">redisClient</span>, <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Background</span>(), <span style="color:#a6e22e">key</span>, <span style="color:#66d9ef">func</span>() ([]<span style="color:#a6e22e">MyExampleType1</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>		 <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">FetchSomethingHeavyFromDB</span>()
</span></span><span style="display:flex;"><span>	   }, <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Hour</span><span style="color:#f92672">*</span><span style="color:#ae81ff">12</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">MyCachedResolver2</span>(<span style="color:#a6e22e">redisClient</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">redis</span>.<span style="color:#a6e22e">Client</span>) ([]<span style="color:#a6e22e">MyExampleType2</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>       <span style="color:#a6e22e">key</span> <span style="color:#f92672">:=</span> <span style="color:#e6db74">&#34;resolver_result_2&#34;</span>
</span></span><span style="display:flex;"><span>       <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">WithCache</span>[[]<span style="color:#a6e22e">MyExampleType2</span>](<span style="color:#a6e22e">redisClient</span>, <span style="color:#a6e22e">context</span>.<span style="color:#a6e22e">Background</span>(), <span style="color:#a6e22e">key</span>, <span style="color:#66d9ef">func</span>() ([]<span style="color:#a6e22e">MyExampleType2</span>, <span style="color:#66d9ef">error</span>) {
</span></span><span style="display:flex;"><span>		 <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">FetchSomethingEvenMoreHeavierFromDB</span>()
</span></span><span style="display:flex;"><span>	   }, <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Hour</span><span style="color:#f92672">*</span><span style="color:#ae81ff">12</span>)
</span></span><span style="display:flex;"><span>     }
</span></span></code></pre></div><p>Now it looks much better and clearer!</p>
<p>The caching code this example refactors is the one from <a href="/posts/ristretto-the-most-performant-concurrent-cache-library-for-go/">Ristretto — the most performant concurrent cache library for Go</a>, if you want the full context. For another place where a little type machinery removes a lot of duplication, see <a href="/posts/implementing-enums-in-golang/">implementing enums in Golang</a>. And since Go 1.27 a method can carry its own type parameters, which removes the package-level helper this article works around — <a href="/posts/whats-new-in-go-1-27/">what&rsquo;s new in Go 1.27</a>.</p>]]></content:encoded>
      <category>Go Programming</category>
      <category>Backend Development</category>
    </item>
    <item>
      <title>Ristretto - the Most Performant Concurrent Cache Library for Go</title>
      <link>https://webdevstation.com/posts/ristretto-the-most-performant-concurrent-cache-library-for-go/</link>
      <pubDate>Tue, 02 Mar 2021 18:19:53 +0100</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/ristretto-the-most-performant-concurrent-cache-library-for-go/</guid>
      <description>Learn how to implement Ristretto, a high-performance concurrent memory caching library for Go applications. Includes code examples comparing database access with and…</description>
      <content:encoded><![CDATA[<p>Recently, I discovered a surprisingly reliable memory caching solution, which I&rsquo;m planning to use in all my further applications to increase performance. In this blog post, I will share some code examples of how you can integrate <a href="https://github.com/dgraph-io/ristretto">Ristretto</a> caching library into your application.</p>
<p><code>Ristretto is a fast, concurrent cache library built with a focus on performance and correctness.</code></p>
<p>This library was created by the Dgraph team as a contention-free cache for the Dgraph database.</p>
<p>Let&rsquo;s dive into the practical example. We are going to build a simple application that gets a list of users from the database. In the first iteration, there will be no caching layer at all. In the second iteration, we will add a Ristretto caching and compare execution time.</p>
<p>Below, you can see that I defined a <code>repository</code> package with the <code>Repository</code> interface and with <code>InMemoryRepository</code> implementation:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">repository</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> <span style="color:#e6db74">&#34;fmt&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// Repository interface to handle users data.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">Repository</span> <span style="color:#66d9ef">interface</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">GetUsers</span>() <span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">int</span>]<span style="color:#66d9ef">string</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">InMemoryRepository</span> <span style="color:#66d9ef">struct</span>{}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// NewInMemoryRepository constructs and returns InMemoryRepository.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">NewInMemoryRepository</span>() <span style="color:#f92672">*</span><span style="color:#a6e22e">InMemoryRepository</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">InMemoryRepository</span>{}
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// GetUsers returns 50000 dummy users from the in-memory repository.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">InMemoryRepository</span>) <span style="color:#a6e22e">GetUsers</span>() <span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">int</span>]<span style="color:#66d9ef">string</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">users</span> <span style="color:#f92672">:=</span> make(<span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">int</span>]<span style="color:#66d9ef">string</span>)
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span><span style="color:#f92672">:=</span><span style="color:#ae81ff">1</span>; <span style="color:#a6e22e">i</span> <span style="color:#f92672">&lt;=</span> <span style="color:#ae81ff">50000</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">users</span>[<span style="color:#a6e22e">i</span>] = <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Sprintf</span>(<span style="color:#e6db74">&#34;User %d&#34;</span>, <span style="color:#a6e22e">i</span>)
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">users</span>
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Next, we are going to call a <code>GetUsers()</code> method 100 times to simulate calling of the same function from several places in the real-world applications:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;fmt&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;github.com/alexsergivan/blog-examples/ristretto/repository&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span><span style="color:#f92672">:=</span><span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span>&lt;<span style="color:#ae81ff">100</span>; <span style="color:#a6e22e">i</span> <span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">UsersGetter</span>(<span style="color:#a6e22e">repository</span>.<span style="color:#a6e22e">NewInMemoryRepository</span>())
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">UsersGetter</span>(<span style="color:#a6e22e">repository</span> <span style="color:#a6e22e">repository</span>.<span style="color:#a6e22e">Repository</span>) {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">repository</span>.<span style="color:#a6e22e">GetUsers</span>()
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Let&rsquo;s measure how much time it takes to execute it with <code>time go run main.go</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>1.46s user
</span></span><span style="display:flex;"><span>0.34s system
</span></span><span style="display:flex;"><span>106% cpu
</span></span><span style="display:flex;"><span>1.686 total
</span></span></code></pre></div><p>Next, we are going to add a caching layer to our application.</p>
<p>Don&rsquo;t forget to get the Ristretto library:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>  go get github.com/dgraph-io/ristretto
</span></span></code></pre></div><p>Inside <code>repository</code> package we inject Ristretto cache:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span><span style="color:#f92672">package</span> <span style="color:#a6e22e">repository</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;fmt&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;github.com/dgraph-io/ristretto&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;time&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">type</span> <span style="color:#a6e22e">InMemoryRepository</span> <span style="color:#66d9ef">struct</span>{
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">cache</span>        <span style="color:#f92672">*</span><span style="color:#a6e22e">ristretto</span>.<span style="color:#a6e22e">Cache</span>
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// NewInMemoryRepository constructs and returns InMemoryRepository.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">NewInMemoryRepository</span>(<span style="color:#a6e22e">ristrettoCache</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">ristretto</span>.<span style="color:#a6e22e">Cache</span>) <span style="color:#f92672">*</span><span style="color:#a6e22e">InMemoryRepository</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#f92672">&amp;</span><span style="color:#a6e22e">InMemoryRepository</span>{
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">cache</span>: <span style="color:#a6e22e">ristrettoCache</span>,
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#75715e">// GetUsers returns 50000 dummy users from the in-memory repository.</span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> (<span style="color:#a6e22e">r</span> <span style="color:#f92672">*</span><span style="color:#a6e22e">InMemoryRepository</span>) <span style="color:#a6e22e">GetUsers</span>() <span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">int</span>]<span style="color:#66d9ef">string</span> {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">key</span> <span style="color:#f92672">:=</span> <span style="color:#e6db74">&#34;users&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">value</span>, <span style="color:#a6e22e">found</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">cache</span>.<span style="color:#a6e22e">Get</span>(<span style="color:#a6e22e">key</span>)
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// If the users data not cached yet, get it from the repository.</span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">if</span> !<span style="color:#a6e22e">found</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">users</span> <span style="color:#f92672">:=</span> make(<span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">int</span>]<span style="color:#66d9ef">string</span>)
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span><span style="color:#f92672">:=</span><span style="color:#ae81ff">1</span>; <span style="color:#a6e22e">i</span> <span style="color:#f92672">&lt;=</span> <span style="color:#ae81ff">50000</span>; <span style="color:#a6e22e">i</span><span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>			<span style="color:#a6e22e">users</span>[<span style="color:#a6e22e">i</span>] = <span style="color:#a6e22e">fmt</span>.<span style="color:#a6e22e">Sprintf</span>(<span style="color:#e6db74">&#34;User %d&#34;</span>, <span style="color:#a6e22e">i</span>)
</span></span><span style="display:flex;"><span>		}
</span></span><span style="display:flex;"><span>        <span style="color:#75715e">// Adds data to the cache for 1h.</span>
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">cache</span>.<span style="color:#a6e22e">SetWithTTL</span>(<span style="color:#a6e22e">key</span>, <span style="color:#a6e22e">users</span>, <span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">1</span><span style="color:#f92672">*</span><span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Hour</span>)
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Sleep</span>(<span style="color:#ae81ff">10</span> <span style="color:#f92672">*</span> <span style="color:#a6e22e">time</span>.<span style="color:#a6e22e">Millisecond</span>)
</span></span><span style="display:flex;"><span>		<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">users</span>
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">return</span> <span style="color:#a6e22e">value</span>.(<span style="color:#66d9ef">map</span>[<span style="color:#66d9ef">int</span>]<span style="color:#66d9ef">string</span>)
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Next, inside the <code>main()</code> function we initiate a new Ristretto cache and pass it to the <code>InMemoryRepository</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-go" data-lang="go"><span style="display:flex;"><span>  <span style="color:#f92672">package</span> <span style="color:#a6e22e">main</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">import</span> (
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;github.com/alexsergivan/blog-examples/ristretto/repository&#34;</span>
</span></span><span style="display:flex;"><span>	<span style="color:#e6db74">&#34;github.com/dgraph-io/ristretto&#34;</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">main</span>() {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">ristrettoCache</span>, <span style="color:#a6e22e">_</span> <span style="color:#f92672">:=</span> <span style="color:#a6e22e">ristretto</span>.<span style="color:#a6e22e">NewCache</span>(<span style="color:#f92672">&amp;</span><span style="color:#a6e22e">ristretto</span>.<span style="color:#a6e22e">Config</span>{
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">NumCounters</span>: <span style="color:#ae81ff">1e7</span>,     <span style="color:#75715e">// Num keys to track frequency of (10M).</span>
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">MaxCost</span>:     <span style="color:#ae81ff">1</span> <span style="color:#f92672">&lt;&lt;</span> <span style="color:#ae81ff">30</span>, <span style="color:#75715e">// Maximum cost of cache (1GB).</span>
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">BufferItems</span>: <span style="color:#ae81ff">64</span>,      <span style="color:#75715e">// Number of keys per Get buffer.</span>
</span></span><span style="display:flex;"><span>	})
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>	<span style="color:#66d9ef">for</span> <span style="color:#a6e22e">i</span><span style="color:#f92672">:=</span><span style="color:#ae81ff">0</span>; <span style="color:#a6e22e">i</span>&lt;<span style="color:#ae81ff">100</span>; <span style="color:#a6e22e">i</span> <span style="color:#f92672">++</span> {
</span></span><span style="display:flex;"><span>		<span style="color:#a6e22e">UsersGetter</span>(<span style="color:#a6e22e">repository</span>.<span style="color:#a6e22e">NewInMemoryRepository</span>(<span style="color:#a6e22e">ristrettoCache</span>))
</span></span><span style="display:flex;"><span>	}
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">func</span> <span style="color:#a6e22e">UsersGetter</span>(<span style="color:#a6e22e">repository</span> <span style="color:#a6e22e">repository</span>.<span style="color:#a6e22e">Repository</span>) {
</span></span><span style="display:flex;"><span>	<span style="color:#a6e22e">repository</span>.<span style="color:#a6e22e">GetUsers</span>()
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Let&rsquo;s check how much time it takes to perform the same action:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>0.29s user
</span></span><span style="display:flex;"><span>0.26s system
</span></span><span style="display:flex;"><span>147% cpu
</span></span><span style="display:flex;"><span>0.377 total
</span></span></code></pre></div><p>As you can notice, the total time is 4 times less than in the example without caching layer.</p>
<p>Despite a silly example, I hope you got an idea of how to integrate the Ristretto caching into your application and how it could improve overall performance.</p>
<p>The complete source code you can find <a href="https://github.com/alexsergivan/blog-examples/tree/master/ristretto">here</a>.</p>
<p>Ristretto is safe for concurrent use, which is exactly the problem a plain map does not solve — see <a href="/posts/concurrent-map-writing-and-reading-in-go/">concurrent map writing and reading in Go</a> if you want the failure mode in detail. I also refactored this caching layer with generics in <a href="/posts/example-of-how-generics-simplify-golang/">how Golang generics minimize the amount of code you need to write</a>. For a cache with a very different failure mode — one that costs you money rather than latency when it silently stops working — see <a href="/posts/prompt-caching-llm-cost/">prompt caching</a>.</p>]]></content:encoded>
      <category>Go Programming</category>
      <category>Performance Optimization</category>
    </item>
    <item>
      <title>An Easy Way to Load Test Your Web Apps</title>
      <link>https://webdevstation.com/posts/an-easy-way-to-loadtest-your-web-apps/</link>
      <pubDate>Fri, 12 Feb 2021 18:56:46 +0000</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/an-easy-way-to-loadtest-your-web-apps/</guid>
      <description>Learn how to implement effective load testing for your web applications using the k6 tool and automate performance testing in your GitLab CI/CD pipeline.</description>
      <content:encoded><![CDATA[<p>This time, I want to share my positive experience of load testing of one of our web services, by using <a href="https://k6.io/">K6</a> tool.
Moreover, we will see how easily we can integrate this into the GitLab CI pipeline.</p>
<p>When you develop web applications, it&rsquo;s crucial to have a testing strategy. Nobody argues about the importance of unit,
functional, and integration testing. Nevertheless, very often developers forget to test how their application works under high load.
Even, when we have a &ldquo;green light&rdquo; from all our testing stages, including manual testing, better to not release it to production, until you load test it.
Otherwise, nobody can guarantee, that application will work properly when 50 users will use it simultaneously.</p>
<p>In this article I&rsquo;m going to load test our web application from the <a href="https://webdevstation.com/posts/how-to-show-flash-messages-in-go-echo/">previous article</a>.</p>
<p>For that, we are going to use a K6 tool, written in Go, and uses JavaScript for scripting.</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-text" data-lang="text"><span style="display:flex;"><span>k6 is a developer-centric, free and open-source load testing tool built for making performance testing a productive and enjoyable experience.
</span></span></code></pre></div><p>Let&rsquo;s install this tool:</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>brew install k6
</span></span></code></pre></div><p>If you have different from the macOS operating system, please read about others ways to install <a href="https://k6.io/docs/getting-started/installation">here</a>.</p>
<p>Next, we create a <code>loadtests</code> folder in the root of our project and inside we add a <code>test.js</code> file, where we are going to write our load tests scenarios:</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-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">sleep</span> } <span style="color:#a6e22e">from</span> <span style="color:#e6db74">&#39;k6&#39;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> <span style="color:#a6e22e">http</span> <span style="color:#a6e22e">from</span> <span style="color:#e6db74">&#39;k6/http&#39;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">check</span> } <span style="color:#a6e22e">from</span> <span style="color:#e6db74">&#39;k6&#39;</span>;
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">import</span> { <span style="color:#a6e22e">Rate</span> } <span style="color:#a6e22e">from</span> <span style="color:#e6db74">&#39;k6/metrics&#39;</span>;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">export</span> <span style="color:#66d9ef">let</span> <span style="color:#a6e22e">errorRate</span> <span style="color:#f92672">=</span> <span style="color:#66d9ef">new</span> <span style="color:#a6e22e">Rate</span>(<span style="color:#e6db74">&#39;errors&#39;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#66d9ef">export</span> <span style="color:#66d9ef">let</span> <span style="color:#a6e22e">options</span> <span style="color:#f92672">=</span> {
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Here we define our scenarios.
</span></span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">scenarios</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">sign_in_page_test</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">executor</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;constant-vus&#39;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">duration</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;1m&#39;</span>,
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">vus</span><span style="color:#f92672">:</span> <span style="color:#ae81ff">100</span>, <span style="color:#75715e">// amount of the virtual users
</span></span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">tags</span><span style="color:#f92672">:</span> { <span style="color:#a6e22e">test_type</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;signInPage&#39;</span> },
</span></span><span style="display:flex;"><span>            <span style="color:#a6e22e">exec</span><span style="color:#f92672">:</span> <span style="color:#e6db74">&#39;signInPage&#39;</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">// List of thresholds.
</span></span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">thresholds</span><span style="color:#f92672">:</span> {
</span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">http_req_duration</span><span style="color:#f92672">:</span> [<span style="color:#e6db74">&#39;avg&lt;500&#39;</span>], <span style="color:#75715e">// avg response times must be below 0.5s
</span></span></span><span style="display:flex;"><span>        <span style="color:#a6e22e">errors</span><span style="color:#f92672">:</span> [<span style="color:#e6db74">&#39;rate&lt;0.1&#39;</span>], <span style="color:#75715e">// &lt;10% errors
</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">export</span> <span style="color:#66d9ef">function</span> <span style="color:#a6e22e">signInPage</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">res</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">http</span>.<span style="color:#a6e22e">get</span>(<span style="color:#a6e22e">getDomain</span>() <span style="color:#f92672">+</span> <span style="color:#e6db74">&#39;/user/signin&#39;</span>);
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// Here we check the response status.
</span></span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">result</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">check</span>(<span style="color:#a6e22e">res</span>, {
</span></span><span style="display:flex;"><span>        <span style="color:#e6db74">&#39;status is 200&#39;</span><span style="color:#f92672">:</span> (<span style="color:#a6e22e">r</span>) =&gt; <span style="color:#a6e22e">r</span>.<span style="color:#a6e22e">status</span> <span style="color:#f92672">==</span> <span style="color:#ae81ff">200</span>,
</span></span><span style="display:flex;"><span>    });
</span></span><span style="display:flex;"><span>    <span style="color:#75715e">// If it&#39;s different from 200, add info to the errorRate.
</span></span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">errorRate</span>.<span style="color:#a6e22e">add</span>(<span style="color:#f92672">!</span><span style="color:#a6e22e">result</span>);
</span></span><span style="display:flex;"><span>    <span style="color:#a6e22e">sleep</span>(<span style="color:#ae81ff">3</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">// Gets the domain from the environment variables.
</span></span></span><span style="display:flex;"><span><span style="color:#66d9ef">function</span> <span style="color:#a6e22e">getDomain</span>() {
</span></span><span style="display:flex;"><span>    <span style="color:#66d9ef">return</span> <span style="color:#a6e22e">__ENV</span>.<span style="color:#a6e22e">DOMAIN</span>;
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>Above, we have declared a scenario to load test <code>/user/signin</code> page with 100 virtual users who continuously accessing our page in parallel.</p>
<p>To run this load test, we need to execute this command in the terminal:</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>k6 run --env DOMAIN<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;http://localhost:8777&#34;</span> ./loadtests/test.js
</span></span></code></pre></div><p>After some time we will see this results output:
<img src="/images/0221/k6.png" alt="k6 terminal output listing checks, request duration percentiles and the passing thresholds for the load test" title="k6 load test results in the terminal"></p>
<p>As you could notice, all our defined thresholds were satisfied. So far so good!</p>
<p>Now, let&rsquo;s see how we can integrate load testing to the Gitlab CI pipeline. Fortunately, it&rsquo;s easy to do :)</p>
<p>Inside <code>.gitlab-ci.yml</code> we need to add this:</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-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#f92672">stages</span>:
</span></span><span style="display:flex;"><span>  - <span style="color:#ae81ff">loadtest</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#f92672">loadtesting</span>:
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">stage</span>: <span style="color:#ae81ff">loadtest</span>
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">image</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">name</span>: <span style="color:#ae81ff">loadimpact/k6:latest</span>
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">entrypoint</span>: [ <span style="color:#e6db74">&#39;&#39;</span> ]
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">variables</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#f92672">DOMAIN</span>: <span style="color:#e6db74">&#39;[your-testing-domain-here]&#39;</span>  
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">script</span>:
</span></span><span style="display:flex;"><span>    - <span style="color:#ae81ff">echo &#34;executing K6 load tests in k6 container...&#34;</span>
</span></span><span style="display:flex;"><span>    - <span style="color:#ae81ff">k6 run --env DOMAIN=${DOMAIN} ./loadtests/test.js</span>
</span></span></code></pre></div><p>That was it! I&rsquo;ve described just an idea how you can easily integrate the load testing in your development routine.
In the real-world situations you might create more complex load testing scenarios, which will help you find weak points of your application and prevent unexpected downtimes.</p>
<p>I wish you happy coding and no pagerduty calls during the night!😉</p>
<p>Once you can measure, you have something to optimise against. Two places I usually look first: <a href="/posts/early-hints-in-go-or-the-way-of-how-to-improve-perforamnce-of-web-page/">103 Early Hints in Go</a> for front-end latency, and <a href="/posts/ristretto-the-most-performant-concurrent-cache-library-for-go/">Ristretto caching</a> for the expensive calls behind it. It is also the right tool to prove your <a href="/posts/graceful-shutdown-in-go-web-services/">graceful shutdown</a> really does keep errors at zero during a deploy.</p>]]></content:encoded>
      <category>DevOps</category>
      <category>Testing</category>
    </item>
    <item>
      <title>How to make Nginx cache cookie aware</title>
      <link>https://webdevstation.com/posts/how-to-make-nginx-cookie-aware/</link>
      <pubDate>Tue, 15 Dec 2020 20:12:51 +0000</pubDate>
      <author>Alex</author>
      <guid isPermaLink="true">https://webdevstation.com/posts/how-to-make-nginx-cookie-aware/</guid>
      <description>Learn how to configure Nginx to cache responses based on specific cookie values, enabling proper A/B testing and personalized content delivery while maintaining…</description>
      <content:encoded><![CDATA[<p>In this post, I&rsquo;m going to describe how we can configure nginx to be able to cache responses based on the specific cookie value.</p>
<p>Let&rsquo;s imagine a situation when you want to do an A/B test on your website, where 50% of users should see new headline text on the page. Other 50% of visitors will continue see the old page. In this case, all your server-side manipulation about splitting users to different versions will be ignored by nginx cache (of course, if you have it).</p>
<p>It happens because nginx, by default, has this configuration:</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-nginx" data-lang="nginx"><span style="display:flex;"><span><span style="color:#66d9ef">proxy_cache_key</span> $scheme$proxy_host$request_uri;
</span></span></code></pre></div><p>So, it will use the same cache key for all users, who requests a page with the same url.</p>
<p>Luckily, nginx allows us easily to customize proxy_cache_key! What we need to do, it&rsquo;s just to add a specific cookie to this key:</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-nginx" data-lang="nginx"><span style="display:flex;"><span><span style="color:#66d9ef">proxy_cache_key</span> $scheme$proxy_host$request_uri$cookie_MY_COOKIE_NAME;
</span></span></code></pre></div><p>And that&rsquo;s it! After this, if userA has MY_COOKIE_NAME=A and userB has MY_COOKIE_NAME=B they will receive different versions of page by the same url.</p>
<p>If you need more complex behaviour, where you won&rsquo;t use hardcoded cookie name in your nginx config, you can do something like this:</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-nginx" data-lang="nginx"><span style="display:flex;"><span><span style="color:#66d9ef">if</span> <span style="color:#e6db74">(</span>$http_cookie ~<span style="color:#e6db74">*</span> <span style="color:#e6db74">&#34;ab_(.*?)=([\w-]+)&#34;</span> <span style="color:#e6db74">)</span> {
</span></span><span style="display:flex;"><span>  <span style="color:#f92672">set</span> $abcookie $1$2;
</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">proxy_cache_key</span> $scheme$proxy_host$request_uri$abcookie; 
</span></span></code></pre></div><p>As you can see, you can use $http_cookie and generate proxy_cache_key based on specific cookie patterns.
In this concrete example we check if http cookies contains cookie with regex <code>pattern ab_(.*?)=([\w-]+)</code> and if this cookie exists, we generate new variable for proxy_cache_key`.</p>
<p>Caching at the proxy is only one layer. For the one inside your Go process, see <a href="/posts/ristretto-the-most-performant-concurrent-cache-library-for-go/">Ristretto — the most performant concurrent cache library for Go</a>, and for shaving latency off the browser side, <a href="/posts/early-hints-in-go-or-the-way-of-how-to-improve-perforamnce-of-web-page/">103 Early Hints in Go</a>.</p>]]></content:encoded>
      <category>DevOps</category>
      <category>Web Development</category>
    </item>
  </channel>
</rss>
