ph.codeberg.page https://ph.codeberg.page 2025-12-19T16:52:32Z Changes on this website. Piotr Husiatyński https://ph.codeberg.page/2025-12-19-changes_on_this_website 2025-12-19T00:00:00Z <p>There are two changes to this website worth mentioning.</p> <p>First, hosting has moved from GitHub to Codeberg.</p> <p>Second, I have switched from Hugo to a custom rendering engine.</p> <p>I never needed the advanced functionality Hugo provides. After an upgrade, my custom theme broke and I could no longer compile this website. Instead of spending hours figuring out and fixing the issue, I decided to finally roll out my own minimal compiler. Excluding the templates, the implementation is less than <a href="https://codeberg.org/ph/pages/src/branch/main/main.go">330 lines of simple Go code</a>, with goldmark as the single dependency to convert Markdown to HTML.</p> <p>The build process and deployment are handled by <a href="https://codeberg.org/ph/pages/src/commit/a1977a805984d1793149965d2dc338c58eb2b76c/run#L15-L30">a bash function</a>, which compiles the content and publishes it via a designated Git branch.</p> <p>The feed URL is now <a href="https://ph.codeberg.page/feed.xml">https://ph.codeberg.page/feed.xml</a>.</p> Routing enhancements. Piotr Husiatyński https://ph.codeberg.page/2024-06-12-routing_enhancements 2024-06-12T00:00:00Z <p>Go 1.22 ships with <a href="https://go.dev/blog/routing-enhancements">router Enhancements</a>. The <a href="https://godocs.io/net/http#ServeMux"><code>net/http.ServeMux</code></a> can now match requests by method, host and a simple path wildcard.</p> <p>With the new ServeMux, it is no longer necessary to <a href="https://benhoyt.com/writings/go-routing/">struggle</a> to find the best routing method. For most cases, standard library should be the best choice. And with the next release, you can align your declarations with <a href="https://github.com/golang/go/commit/7b583fd1a1aeda98daa5a9d485b35786c031e941">any number of spaces</a>.</p> <pre><code class="language-go">func run() { rt := http.NewServeMux() rt.Handle(`POST /users`, &amp;demoHandler{info: &quot;create user&quot;}) rt.Handle(`GET /users/{name}`, &amp;demoHandler{info: &quot;show user&quot;}) rt.Handle(`GET /users/{name}/profile`, &amp;demoHandler{info: &quot;show user profile&quot;}) _ = http.ListenAndServe(&quot;localhost:8000&quot;, rt) } type demoHandler struct { info string } func (h *demoHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, h.info, r.PathValue(&quot;name&quot;)) } </code></pre> <pre><code class="language-sh">% curl localhost:8000/users Method Not Allowed % curl localhost:8000/users -X POST create user % curl localhost:8000/users/andy show user andy % curl localhost:8000/users/andy/profile show user profile andy % curl localhost:8000/users/andy/profile -X POST Method Not Allowed </code></pre> Tasks script. Piotr Husiatyński https://ph.codeberg.page/2024-05-28-tasks_script 2024-05-28T00:00:00Z <p>Every project grows to a point where a set of custom tasks must be executed on various occasions. It is good to write those commands down, so that they don't get lost and anyone can execute them. I used to maintain a <a href="https://makefiletutorial.com/"><em>Makefile</em></a> as a simple way to organize and share tasks with others.</p> <p>As the complexity grows, and more functionality is needed, Makefile becomes more unreadable. It feels like using the wrong tool for the job.</p> <p>And indeed, there is a better tool - shell scripting.</p> <p>The below <a href="https://en.wikipedia.org/wiki/Bash_(Unix_shell)">Bash</a> script is a solid base to extend, for managing a collection of tasks.</p> <pre><code class="language-bash">#!/usr/bin/env bash set -euo pipefail function task:help { # Print this script help. local tasks local self_path local desc printf &quot;%s &lt;task&gt; [args] Tasks: &quot; &quot;${0}&quot; tasks=$(compgen -A function | sed -En 's/task:(.*)//p') self_path=$(realpath &quot;$0&quot;) for task in ${tasks}; do desc=$(grep &quot;function task:$task {&quot; &quot;$self_path&quot; -A 1 | sed -En 's/.*# (.*)//p') printf &quot; %-32s %s &quot; &quot;$task&quot; &quot;$desc&quot; done } # shellcheck disable=SC2145 &quot;task:${@:-help}&quot; </code></pre> <p>In order to register a new task, define a <code>task:&lt;name&gt;</code> function. The first line, when comment, is used as that task documentation.</p> <pre><code class="language-bash">function task:say-hello { # Greet the user. echo &quot;Hello $USER&quot; } </code></pre> <p>When the script is called with no arguments, it runs <code>help</code> that renders the list of available tasks with their description.</p> <pre><code class="language-sh">% ./run ./run &lt;task&gt; [args] Tasks: say-hello Greet the user. help Print this script help. </code></pre> Testing in go. Piotr Husiatyński https://ph.codeberg.page/2020-01-30-testing_in_go 2020-01-30T00:00:00Z <p>This is a collection of testing techniques and patterns that I have learned throughout my career of being a Go programmer.</p> <h2><code>testing</code> package basics</h2> <p>The Go standard library comes with the <a href="https://golang.org/pkg/testing/"><code>testing</code></a> package which provides a solid base for writing tests.</p> <p>Each test should be a separate function. A test function must accept a single argument of type <a href="https://golang.org/pkg/testing/#T"><code>*testing.T</code></a>.</p> <p>A test for a functoin <code>isEven</code> could look like this:</p> <pre><code class="language-go">func TestIsEven(t *testing.T) { if !isEven(2) { t.Fatal(&quot;2 is even&quot;) } if isEven(1) { t.Fatal(&quot;1 is odd&quot;) } } </code></pre> <p>Run your test by using the <a href="https://golang.org/cmd/go/#hdr-Test_packages"><code>go test</code></a> command, for example</p> <pre><code class="language-sh"># Test this directory $ go test . # Test the whole project recursively. $ go test a-package.com/path/... </code></pre> <h3>Failing and messages</h3> <p>Each test accepts one argument, a <code>T</code> instance. <code>T</code> provides methods that allow to print information and control the flow of a test.</p> <p>Use <code>t.Log</code> and <code>t.Logf</code> methods to write a message.</p> <p>Use <code>t.Error</code> and <code>t.Errorf</code> methods to write a message and mark the test as failed.</p> <p>Use <code>t.Fatal</code> and <code>t.Fatalf</code> methods to write a message, mark the test as failed and instantly terminate that test execution.</p> <h4>Write good error messages</h4> <p>A good error message is concise and short. Sprinkle each result with a bit of context.</p> <pre><code class="language-go">if isEven(1) { t.Fatal(&quot;1 is an odd number&quot;) } if want, got := 42, compute(); want != got { t.Fatalf(&quot;want %d, got %d&quot;, want, got) } </code></pre> <p>By declaring <code>got</code> and <code>want</code> I am sure that what is tested for is what I print. If the <code>compute</code> function was changed and in the new implementation <code>want</code> should be <code>33</code> I cannot make the mistake of not updating the error message. Both <code>got</code> and <code>want</code> are scoped to the <code>if</code> statement only.</p> <p>When writing a table test, declaring an expected value might not be necessary. The expected value can be easily found in the test declaration.</p> <h3>Skipping a test</h3> <p>Some tests should run only under special circumstances. For example, you want to run a test only if a database is available. <code>t.Skip</code> and <code>t.Skipf</code> methods allow to cancel (skip) the currently running test without failing it.</p> <pre><code class="language-go">func TestDatabaseIntegration(t *testing.T) { db, err := connectToDatabase(&quot;test-database&quot;) if err != nil { t.Skipf(&quot;cannot connect to database: %s&quot;, err) } defer db.Close() // ... } </code></pre> <h3>Test helpers</h3> <p>Often times many tests require similar dependencies, for example running a service or preparing a state. Instead of repeating the preparation code extract each functionality to a separate function.</p> <h3>Test helpers: Setting up dependencies</h3> <p>If you are testing code that depends on an external database, this is how the beginning of a test function might look like:</p> <pre><code class="language-go">func TestDatabaseIntegration(t *testing.T) { db, err := connectToDatabase(&quot;test-database&quot;) if err != nil { t.Skipf(&quot;cannot connect to database: %s&quot;, err) } defer db.Close() if err := db.Ping(); err != nil { t.Fatalf(&quot;cannot ping database: %s&quot;, err) } for i, migration := range databaseMigrations { if err := db.ApplyMigration(migration); err != nil { t.Fatalf(&quot;cannot apply %d migration: %s&quot;, i, err) } } mycollection := NewCollection(db) // The actual test starts below. // ... } </code></pre> <p>A solution to code repetition can be to create a function that will encapsulate certain functionality. The whole setup and teardown process for a test can be extracted.</p> <pre><code class="language-go">func TestDatabaseIntegration(t *testing.T) { mycollection, cleanup := ensureMyCollection(t, &quot;test-database&quot;) defer cleanup() // The actual test starts below. // ... } func ensureMyCollection(t testing.TB), dbName string (MyCollection, func(){} { t.Helper() db, err := connectToDatabase(dbName) if err != nil { t.Skipf(&quot;cannot connect to database: %s&quot;, err) } if err := db.Ping(); err != nil { db.Close() t.Fatalf(&quot;cannot ping database: %s&quot;, err) } for i, migration := range databaseMigrations { if err := db.ApplyMigration(migration); err != nil { db.Close() t.Fatalf(&quot;cannot apply %d migration: %s&quot;, i, err) } } collection := NewCollection(db) cleanup := func() { db.Close() } return collection, cleanup } </code></pre> <p>With the above solution, <code>ensureMyCollection</code> can be used by many test functions to ensure that a collection using a database as a backend is available. A helper function hides the for the test logic irrelevant part of setting up an environment and ensuring all components are provided.</p> <p>A helper function accepts <a href="https://golang.org/pkg/testing/#TB"><code>testing.TB</code></a> interface instead of <code>t *testing.T</code>. That makes it useful for both test and <a href="https://golang.org/pkg/testing/#hdr-Benchmarks">benchmark functions</a>.</p> <p>A helper function does not return an error. Instead, it directly terminates the test by calling <code>t.Fatal</code>.</p> <p>At the beginning of the helper function the <a href="https://golang.org/pkg/testing/#T.Helper"><code>t.Helper()</code></a> method is called. This marks this function and when it fails the stack information and error will be more helpful.</p> <p><code>ensureMyCollection</code> returns a cleanup function. This is a convenient way of cleaning up all created resources. The user of this helper must call it once the returned resource is not needed anymore. The cleanup function should not return anything nor fail the test.</p> <h3>Blackbox package testing</h3> <blockquote> <p>Test files that declare a package with the suffix &quot;_test&quot; will be compiled as a separate package, and then linked and run with the main test binary. -- <a href="https://golang.org/cmd/go/#hdr-Test_packages">golang.org</a></p> </blockquote> <p>Test files for your package are located in the same directory as the code they test. Your tests can belong to the same package as the rest of the code. It is also possible to enforce a black-box test for your package. Your test files can be in the same directory as your package code and use a different package name.</p> <pre><code class="language-go">package xxx_test </code></pre> <p>Using a different test package name enforces that only the public interface of the tested package is accessible. This is for example <a href="https://golang.org/src/strings/compare_test.go">how <code>strings</code></a> and <a href="https://golang.org/src/bytes/reader_test.go"><code>bytes</code> packages</a> are tested.</p> <h3>Third party test helper packages</h3> <p>I do not use any additional packages for testing. I am of an opinion that <a href="https://golang.org/doc/faq#testing_framework">assert functions are not as helpful as one may think</a>. Introducing an external package requires learning a new API.</p> <p>Someone else wrote <a href="https://web.archive.org/web/20210411084609/https://danmux.com/posts/the_cult_of_go_test/">a great summary</a> on the topic.</p> <p>Complex comparisons can usually be done using <a href="#reflectdeepequal"><code>reflect.DeepEqual</code></a> function.</p> <h2><code>reflect.DeepEqual</code></h2> <p>Those values that cannot be compared with <code>==</code>, most of the time can be compared with <a href="https://golang.org/pkg/reflect/#DeepEqual"><code>reflect.DeepEqual</code></a>.</p> <h2>Table tests</h2> <p>When testing a functionality a single input is often not enough to ensure correctness. Repeating the same operation for many cases can be implemented using <a href="https://github.com/golang/go/wiki/TableDrivenTests">table tests</a>.</p> <p>Use a map with strings as keys to provide a description of each test case.</p> <pre><code class="language-go">func TestDiv(t *testing.T) { cases := map[string]struct{ A int B int WantRes int WantErr error }{ &quot;two positive numbers&quot;: { A: 4, B: 2, WantRes: 2, }, &quot;divide by zero&quot;: { A: 4, B: 0, WantErr: errors.ErrZeroDivision, }, } for testName, tc := range cases { t.Run(testName, func(t *testing.T) { res, err := Div(tc.A, tc.B) if !errors.Is(err, tc.WantErr) { t.Fatalf(&quot;unexpected error: %q&quot;, err) } if res != tc.WantRes { t.Fatalf(&quot;unlexpected result: %d&quot;, res) } }) } } </code></pre> <p>When declaring a test case, always use field names. This increases the readability and you have to provide only non zero values.</p> <pre><code class="language-go">cases := map[string]struct{ DB *Database Req *Request WantRes int WantErr error }{ // BAD {nil, myrequest, 32, nil}, // GOOD { Req: myrequest, WantRes: 32, }, } </code></pre> <h2>Mocking</h2> <p>Write your code to accept interfaces. Using interfaces allows you to test a single layer of a functionality at a time.</p> <p>For example, if you are writing an application that is storing data in an SQL database, instead of accessing the database directly through a <code>*sql.DB</code> instance <a href="../accessing-data-in-go/#mocking-for-tests">use a wrapper</a>. Using a data access abstraction allows for mocking.</p> <p>When writing a mock you do not have to implement all methods. For the compiler it is enough to include the interface in the mock declaration. Implement only methods that you intend to call.</p> <pre><code class="language-go">type Collection interface { One(id uint64) (*Entity, error) List() ([]*Entity, error) Add(Entity) error Delete(id uint64) error } type CollectionMock struct { Collection Err error } func (c *CollectionMock) Add(Entity) error { return c.Err } </code></pre> <p><code>CollectionMock</code> implements the <code>Collection</code> interface, but using any other method than <code>Add</code> will panic. See <a href="https://play.golang.org/p/GVc2tOJoAHX">the full example</a>.</p> <h3>Your code should provide a mock</h3> <p>When writing a package that is used by others provide test implementations of your interfaces.</p> <p>This approach is taken by the standard library. For example, <a href="https://golang.org/pkg/net/http/httptest/#ResponseRecorder"><code>httptest.ResponseRecorder</code></a> allows to test your HTTP handler without using a real <code>http.ResponseWriter</code>.</p> <h2>Test flags</h2> <p>You can add your own flags to the <code>go test</code> command in order to customize your tests. Use the <a href="https://golang.org/pkg/flag/"><code>flag</code></a> package and declare your flags globally.</p> <pre><code class="language-go">var dbFl = flag.String(&quot;db&quot;, &quot;&quot;, &quot;Use given database DSN.&quot;) </code></pre> <h2>Environment variables</h2> <p>Instead of <code>flag</code> you can control your tests using environment variables. If you follow the <a href="https://12factor.net/config">12 factor app</a> principles then your application is already utilizing environment variables for the configuration.</p> <pre><code class="language-go">var dbDSN = os.Getenv(&quot;DATABASE_DSN&quot;) </code></pre> <h2>Fixtures</h2> <p>If your test requires fixtures <code>/testdata</code> is the directory you should consider keeping them in.</p> <blockquote> <p>The go tool will ignore a directory named &quot;testdata&quot;, making it available to hold ancillary data needed by the tests. -- <a href="https://golang.org/cmd/go/#hdr-Test_packages">golang.org</a></p> </blockquote> <p>When running tests each test function is executed with its working directory set to the source directory of the tested package. That means that when accessing files in <code>/testdata</code> you can safely use relative path</p> <pre><code class="language-go">fd, err := os.Open(filepath.Join(&quot;testdata&quot;, &quot;some-fixture.json&quot;)) </code></pre> <h2>Golden files</h2> <p><a href="https://softwareengineering.stackexchange.com/q/358786">Golden files</a> are a great way to validate and keep track of a test output. Together with a version control system they are much easier to maintain than strings hard coded in functions.</p> <pre><code class="language-go">var goldFl = flag.Bool(&quot;gold&quot;, false, &quot;Write result to golden files instead of comparing with them.&quot;) func TestExample(t *testing.T) { // Test logic. result := ... if *goldFl { writeGoldenFile(t, result) } compareWithGoldenFile(t, result) } </code></pre> <p>This technique comes in very helpful combined with <a href="#table-tests">table tests</a>.</p> <h2>Integration tests</h2> <p>For a well written application <a href="https://en.wikipedia.org/wiki/Integration_testing">integration testing</a> should not require more work than usual testing. For each external resource provide a single function to <a href="#test-helpers-setting-up-dependencies">setup and teardown the resource</a>.</p> <h2>Build constraints</h2> <p>You can use a <a href="https://golang.org/pkg/go/build/#hdr-Build_Constraints">build constraint</a> to conditionally build code in a file.</p> <pre><code class="language-sh">$ head -n 1 app_intergration_test.go // +build integration </code></pre> <p>To run tests including those tagged as <code>integration</code> use <code>-tag</code> flag.</p> <pre><code class="language-sh">$ go test -tag integration . </code></pre> <h2>Setup/teardown</h2> <p>When using the <code>testing</code> package, it is possible to overwrite the <a href="https://golang.org/pkg/testing/#hdr-Main"><code>test main</code></a> function.</p> <p>Using a custom test main function allows to execute code before and after executing all discovered tests. This can be running an external dependency like a database instance or building a binary that tested functionality might depend on.</p> <pre><code class="language-go">func TestMain(m *testing.M) { // Setup code. // defer Teardown code. os.Exit(m.Run()) } </code></pre> <h2><code>-race</code></h2> <p>Run tests with <code>-race</code> flag to enable data race detection.</p> <p>This functionality is not available on <a href="https://www.musl-libc.org/">musl</a> based systems.</p> <h2>Testing FAQ</h2> <p>Check the <a href="https://golang.org/doc/faq#Packages_Testing">FAQ at golang.org</a>.</p> Error handling. Piotr Husiatyński https://ph.codeberg.page/2019-02-20-error_handling 2019-02-20T00:00:00Z <p><em>In Go 1.13 <a href="https://golang.org/doc/go1.13#error_wrapping">error wrapping</a> was introduced as part of the standard library. This post was written before the update to the <code>errors</code> package.</em></p> <p>Go is a language that does not provide exceptions. Instead, an operation can return an error. <a href="https://blog.golang.org/errors-are-values">Errors are values</a> that implement the <code>error</code> interface.</p> <p>I have worked with several errors handling patterns over the years and I would like to summarize my experience focusing on the good solutions.</p> <p>For the purpose of this post, let us imagine a very simple banking application. Accounts are represented by their numeric ID and we only know how much money each account holds. No account balance can get below zero. A bank service must implement the interface below.</p> <pre><code class="language-go">type BankService interface { // NewAccount registers a new account in this bank. The account is // initialized with given funds. NewAccount(accountID int64, funds uint64) error // Transfer moves funds between two accounts. It fails if an operation // would cause the balance of the source account to go below zero. Transfer(from, to int64, amount uint64) error } </code></pre> <p>To keep the examples short and simple an in-memory storage is used. Anything more serious would use a database instead.</p> <h2>Inline error creation</h2> <p>It is a common thing to create errors using <code>errors.New</code> and <code>fmt.Errorf</code> as they are needed. When an operation fails you can handle the failure by creating an error instance and returning it. The created error should contain information about the cause of the failure. With that in mind let us create the first version of a banking service.</p> <p>{{&lt; highlight go &quot;hl_lines=14 24 26 30&quot; &gt;}} func NewBank() *Bank { return &amp;Bank{ accounts: make(map[int64]uint64), } }</p> <p>type Bank struct { accounts map[int64]uint64 }</p> <p>// Create new account with given funds. Account ID must be unique. func (b *Bank) NewAccount(accountID int64, funds uint64) error { if _, ok := b.accounts[accountID]; ok { return errors.New(&quot;account exists&quot;) } b.accounts[accountID] = funds return nil }</p> <p>// Transfer moves funds from one account to another. func (b *Bank) Transfer(from, to int64, amount uint64) error { switch fromFunds, ok := b.accounts[from]; { case !ok: return fmt.Errorf(&quot;source account %d not found&quot;, from) case fromFunds &lt; amount: return fmt.Errorf(&quot;cannot transfer %d from %d account: insufficient funds&quot;, amount, fromFunds) }</p> <pre><code>if _, ok := b.accounts[to]; !ok { return fmt.Errorf(&quot;destination account %d not found&quot;, to) } b.accounts[from] -= amount b.accounts[to] += amount return nil </code></pre> <p>} {{&lt; /highlight &gt;}}</p> <p>Above code presents a common way of dealing with errors. If a failure cannot be dealt with then return the error. If possible provide additional information, for example, an account ID. This is often an acceptable solution but sometimes it might not be good enough. As soon as we use the <code>Bank</code> instance the shortcomings are more visible.</p> <pre><code class="language-go">bank := NewBank() // ... if err := bank.Transfer(111, 222, 10); err != nil { // Why did the transfer fail? } </code></pre> <p>If the <code>Transfer</code> call returns an error it is not possible to learn about the reason and distinguish different cases. As a human analyzing the text message, we can tell what went wrong. If you want your code to react differently if one of the accounts does not exist and do something else when there are not enough funds on the source account then you have a problem.</p> <h2>Predefined errors</h2> <p>To provide more insights into the <code>Transfer</code> method failures one may declare all expected errors upfront.</p> <p>For each failure case declare a corresponding error instance. Compare an error returned by the <code>Transfer</code> method with all error definitions it can return to discover the cause.</p> <p>{{&lt; highlight go &quot;hl_lines=9 11 15&quot; &gt;}} // Transfer moves funds from one account to another. // Upon failure returns one of // ErrNoSourceAccount // ErrNoDestinationAccount // ErrInsufficientFunds func (b *Bank) Transfer(from, to int64, amount uint64) error { switch fromFunds, ok := b.accounts[from]; { case !ok: return ErrNoSourceAccount case fromFunds &lt; amount: return ErrInsufficientFunds }</p> <pre><code>if _, ok := b.accounts[to]; !ok { return ErrNoDestinationAccount } b.accounts[from] -= amount b.accounts[to] += amount return nil </code></pre> <p>}</p> <p>var ( // ErrNoSourceAccount is returned when the source account does not // exist. ErrNoSourceAccount = errors.New(&quot;no source account&quot;)</p> <pre><code>// ErrNoDestinationAccount is returned when the destination account // does not exist. ErrNoDestinationAccount = errors.New(&quot;no destination account&quot;) // ErrInsufficientFunds is returned when a transfer cannot be completed // because there are not enough funds on the source account. ErrInsufficientFunds = errors.New(&quot;insufficient funds&quot;) </code></pre> <p>) {{&lt; /highlight &gt;}}</p> <p>This is similar to how the <a href="https://golang.org/pkg/io/#pkg-variables"><code>io</code></a> package deals with errors.</p> <p>Returning a different error instance for each error case allows us to handle different failure cases accordingly. Test the returned error for being one of the predefined instances.</p> <pre><code class="language-go">bank := NewBank() // ... switch err := bank.Transfer(1, 2); err { case nil: println(&quot;money transferred&quot;) case ErrNoSourceAccount: panic(&quot;source account does not exist&quot;) case ErrNoDestinationAccount: panic(&quot;destination account does not exist&quot;) case ErrInsufficientFunds: panic(&quot;not enough money&quot;) default: panic(&quot;unexpected error&quot;) } </code></pre> <p>This is in my opinion a step in the right direction but it is too verbose. This patten requires too much code to be written. You can no longer create errors when you need them. All failure cases and respective errors must be declared upfront.</p> <p>In addition, you are losing the context information that you were building using <code>fmt.Errorf</code>. When returning <code>ErrInsufficientFunds</code> you no longer know which account caused it. <code>fmt.Errorf</code> must no longer be used for the error instance comparison to work.</p> <h2>Error inheritance</h2> <p>In Python - a language with exceptions and type inheritance - <a href="https://docs.python.org/3/library/exceptions.html#exception-hierarchy">exceptions form a hierarchy</a>. Because each error is an instance of a class belonging to that class hierarchy each exception instance can contain a custom message and be captured by its type or any type it inherits from.</p> <p>This is how a banking service could be used if implemented in Python.</p> <pre><code class="language-python">try: bank.transfer(from, to, amount) except ErrAccountNotFound as e: print(e) # either source or destination account not found except ErrInsufficientFunds: print(&quot;not enough money&quot;) except Exception: print(&quot;unexpected condition&quot;) </code></pre> <p>Because in Python implementation both <code>ErrNoSourceAccount</code> and <code>ErrNoDestinationAccount</code> would inherit from <code>ErrAccountNotFound</code>, both cases can be handled with a single statement <code>except ErrAccountNotFound</code>.</p> <p>When capturing an exception <code>e</code> refers to the exception instance containing the detailed information that can be helpful during debugging or consumed by the client. It can contain more information than just a human readable description.</p> <h3><code>Causer</code> interface</h3> <p>Inheritance is not a requirement to achieve the functionality provided by Python exceptions. When considering an error it is enough if we are able to tell what was the cause of it. This is not possible with errors created using the standard library (<code>errors</code> or <code>fmt</code> packages). Instead of using the standard library, we must create our own error implementation.</p> <p>What is needed is an <code>Error</code> structure that implements the <a href="https://golang.org/pkg/builtin/#error"><code>error</code></a> interface and a <code>Wrap</code> function that will take an error together with an additional description.</p> <pre><code class="language-go">// Wrap returns an error that is having given error set as the cause. func Wrap(err error, description string, args ...interface{}) *Error { return &amp;Error{ parent: err, desc: fmt.Sprintf(description, args...), } } type Error struct { // Parent error if any. parent error // This error description. desc string } func (e *Error) Error() string { if e.parent == nil { return e.desc } return fmt.Sprintf(&quot;%s: %s&quot;, e.desc, e.parent) } </code></pre> <p>In addition, it will provide a <code>Cause</code> method that will return the wrapped error instance or <code>nil</code>.</p> <pre><code class="language-go">// Cause returns the cause of this error or nil if this is the root cause // error. func (e *Error) Cause() error { return e.parent } </code></pre> <p>One more function is necessary for this to be complete. We must be able to compare an error with another error or its cause. The <code>error</code> interface does not provide <code>Cause</code> method so we must use type casting to determine if an error instance implements the <code>causer</code> interface.</p> <p>Instead of a function a method of the <code>Error</code> structure provides a nicer API.</p> <pre><code class="language-go"> // Is returns true if given error or its cause is the same kind. // If cause error provides Cause method then a comparison is made with all // parents as well. func (kind *Error) Is(err error) bool { type causer interface { Cause() error } for { if err == kind { return true } if e, ok := err.(causer); ok { err = e.Cause() } else { return false } } } </code></pre> <p>Let us test the <code>Error</code>. All errors are created using the <code>Wrap</code> function which builds an error hierarchy. It is possible to attach additional information by including it in the description string.</p> <pre><code class="language-go">root := Wrap(nil, &quot;root&quot;) child1 := Wrap(root, &quot;child one&quot;) child2 := Wrap(root, &quot;child two&quot;) fmt.Println(&quot;child 1 is root&quot;, root.Is(child1)) // child 1 is root true fmt.Println(&quot;child 2 is root&quot;, root.Is(child2)) // child 2 is root true fmt.Println(&quot;root is child 1&quot;, child1.Is(root)) // root is child 1 false fmt.Println(&quot;child 2 is child 1&quot;, child1.Is(child2)) // child 2 is child 1 false inlinedErr := Wrap(child2, &quot;current time: %s&quot;, time.Now()) fmt.Println(&quot;inlined child 2 is root&quot;, root.Is(inlinedErr)) // inlined child 2 is root true fmt.Println(&quot;inlined child 2 is child 2&quot;, child2.Is(inlinedErr)) // inlined child 2 is child 2 true fmt.Println(&quot;fmt error is root&quot;, root.Is(fmt.Errorf(&quot;fmt error&quot;))) // fmt error is root false </code></pre> <p>Above <code>Error</code> implementation is a powerful solution to error handling. It is easy to implement, does not require much code and it is portable without creating an explicit dependency on the <code>causer</code> interface.</p> <h2>Predefined errors with inheritance</h2> <p>If an error implements the <code>causer</code> interface we can unwind it and retrieve the previous error instance! This means that no matter how many times we will wrap an error, as long as all layers implement the <code>causer</code> interface we can retrieve the parent error instance.</p> <p>Back to the <code>Bank.Transfer</code> example. All error instances were wrapped before returning and provide all the details one may expect an error to provide.</p> <p>{{&lt; highlight go &quot;hl_lines=4 6-7 11&quot; &gt;}} func (b *Bank) Transfer(from, to int64, amount uint64) error { switch fromFunds, ok := b.accounts[from]; { case !ok: return Wrap(ErrNoSourceAccount, &quot;ID %d&quot;, from) case fromFunds &lt; amount: return Wrap(ErrInsufficientFunds, &quot;cannot transfer %d from %d account&quot;, amount, fromFunds) }</p> <pre><code>if _, ok := b.accounts[to]; !ok { return Wrap(ErrNoDestinationAccount, &quot;ID %d&quot;, to) } b.accounts[from] -= amount b.accounts[to] += amount return nil </code></pre> <p>}</p> <p>var ( // ErrAccountNotFound is return when an operation fails because the // requested account does not exist. ErrAccountNotFound = Wrap(nil, &quot;account not found&quot;)</p> <pre><code>// ErrNoSourceAccount is returned when the source account does not // exist. ErrNoSourceAccount = Wrap(ErrAccountNotFound, &quot;no source&quot;) // ErrNoDestinationAccount is returned when the destination account // does not exist. ErrNoDestinationAccount = Wrap(ErrAccountNotFound, &quot;no destination&quot;) // ErrInsufficientFunds is returned when a transfer cannot be completed // because there are not enough funds on the source account. ErrInsufficientFunds = Wrap(nil, &quot;insufficient funds&quot;) </code></pre> <p>) {{&lt; /highlight &gt;}}</p> <p>Errors can be tested on any granularity level. It is valid to compare with the high level <code>ErrAccountNotFound</code> or more precise <code>ErrNoSourceAccount</code>.</p> <pre><code class="language-go">bank := NewBank() // ... switch err := bank.Transfer(1, 2, 100); { case err == nil: println(&quot;money transferred&quot;) case ErrNoDestinationAccount.Is(err): panic(&quot;destination account does not exist&quot;) case ErrInsufficientFunds.Is(err): panic(&quot;not enough money &quot; + err.Error()) // err provides more details default: panic(&quot;unexpected error&quot;) } </code></pre> <h2>Don't Drink Too Much Cool Aid</h2> <p>What I have presented is a powerful pattern. You may use the <code>causer</code> interface to extract attributes or custom error implementations that were wrapped, attaching helpful information on each execution step. This might be great for example during input validation, where together with an error you want to return information about the invalid fields in a way that can be extracted later.</p> <p>You can use the <code>causer</code> interface and the <code>Wrap</code> function to declare a complex tree of errors that are several layers deep and cover every possible case. If you do, think again about your use case and if such granularity is helpful. Usually, just a handful of errors declared upfront do the job better. I tend to always inline error creation first and only if a case requires more attention declare a previously inlined error.</p> <p>Regardless of what you do try to avoid blindly importing any error package. Consider your use cases and try to <a href="https://commandcenter.blogspot.com/2017/12/error-handling-in-upspin.html">tailor your errors implementation to suit your needs</a>.</p> Cache stampede protection. Piotr Husiatyński https://ph.codeberg.page/2018-09-14-cache_stampede_protection 2018-09-14T00:00:00Z <p>Writing an application that handles concurrent traffic from a lot of clients in a performant way is not an easy task. To narrow this problem to web applications only, serving as many HTTP requests as possible in a short time is often a challenge.</p> <p>In most cases of an HTTP application, optimizing access to the database can be the easiest and the best first step.</p> <h2>Caching database access</h2> <p>Using a database that provides plenty of functionality and storing data in <a href="https://en.wikipedia.org/wiki/Denormalization">denormalized form</a> makes development easier. This comes at the cost of the database having to execute complex queries and do more computation in order to return a result.</p> <p>Making several database queries, even simple ones, to handle a request adds up and makes our request handing slower. Even if a query is executed instantly, the database client must transfer the data over the network each time.</p> <p>Most databases implement some kind of internal caching. They optimize access to popular data if the query complexity allows to do so. Why add an external cache layer in front of the database then?</p> <p>A cache layer can be added to remember:</p> <ol> <li>the result of a <strong>heavy query</strong> that takes time and puts heavy load on the database.</li> <li>the result of a <strong>repeating query</strong> that causes the database to waste resources on returning the same data all the time.</li> </ol> <h3>The fastest code is the code that never runs</h3> <p>Imagine a very popular web application that displays the details of an <em>item</em>. An <em>item</em> is an entity identifiable by a unique number, that is rarely changing. Due to heavy traffic, the database that stores <em>items</em> is all the time asked about the same entity.</p> <p><img src="./statics/direct-access.svg" alt="Direct data access"></p> <p>To offload some of the repeating requests, we introduce a cache layer. Whenever an item is needed, serve it from the cache. The database is queried only if an item does not exist in the cache.</p> <p><img src="./statics/cached-access.svg" alt="Cached data access"></p> <p>For the purpose of this post, let us assume we have a store and a cache implementation available that implement the following interfaces. Delegating <a href="./2018-09-02-accessing_data_in_go.html">database access</a> allows for a cache implementation that is not tightly coupled to the original implementation.</p> <pre><code class="language-go">type ItemStore interface { // FindItem item returns an item with given ID or ErrNotFound FindItem(ctx context.Context, itemID int64) (*Item, error) } type CacheStore interface { // Get loads value under given key into destValue. ErrMiss is returned // if key does not exist. Get(ctx context.Context, key string, destValue interface{}) error // Set value of given key. Set(ctx context.Context, key string, value interface{}, ttl time.Duration) error } </code></pre> <p>Instead of directly calling the database each time an <em>item</em> is needed, a cache layer is used.</p> <pre><code class="language-go">func CacheItemStore(cache CacheStore, store ItemStore) ItemStore { return &amp;cachedItemStore{ store: store, cache: cache, } } type cachedItemStore struct { store ItemStore cache CacheStore } func (c *cachedItemStore) FindItem(ctx context.Context, itemID int64) (*Item, error) { cacheKey := fmt.Sprintf(&quot;item:%d&quot;, itemID) var item Item switch err := c.cache.Get(ctx, cacheKey, &amp;item); err { case nil: return &amp;item, nil case ErrMiss: // Not in cache, fetch from the database. default: // Cache error is not critical for providing this // functionality, log it and continue. log.Printf(&quot;cannot get from cache: %s&quot;, err) } item, err := c.store.FindItem(ctx, itemID) // To simplify this example, do not cache ErrNotFound. Depending on the use // case, remembering that an item does not exist might be desired. if err == nil { if err := c.cache.Set(ctx, cacheKey, &amp;item, time.Minute); err != nil { // Cache error is not critical for providing this // functionality, log it and continue. log.Printf(&quot;cannot set in cache: %s&quot;, err) } } return item, err } </code></pre> <p>Cache errors are not critical for the functionality of the <code>FindItem</code> method. They are logged, so that we have a good insight into our application.</p> <p>To decide for how long a result can be cached and when the value must be refreshed, a domain knowledge is required.</p> <h2>Cache stampede problem</h2> <p>Adding a cache layer can reduce the amount of calls to a resource that the cache protects. Whether the resource is a database, an external service or a local computation task, the amount of communication that happens can be significantly reduced.</p> <blockquote> <p>There are two hard things in computer science: cache invalidation, naming things, and off-by-one errors.</p> <p>-- <a href="https://twitter.com/codinghorror/status/506010907021828096">Jeff Atwood</a></p> </blockquote> <p>Most cache implementations store data with an expiration time, after which it is removed. Using <a href="https://en.wikipedia.org/wiki/Time_to_live">time to live (TTL)</a> is an easy compromise to ensure that stored data is never too old. Instead of trying to keep track of when a certain query result is changing, remember the result for a short period to minimize the possible errors.</p> <p>Cache expiration introduces a new problem. Take our example of a web application all the time displaying an item with ID 1. Hundreds of requests per second and all of them require this item's details to be served. When the item with ID 1 is served from a cache, the database can allocate resources to do something else.</p> <p><img src="./statics/cached-access.svg" alt="Cached data access"></p> <p>Our cache is using TTL to ensure that served data is never too old. <code>FindItem</code> will cache the result for one minute. After one minute, the value expires (it is being removed from the cache) to force a refresh.</p> <p>Keep in mind, that there are hundreds of requests happening every second. All of them need the item with ID 1 to be served. The item is not in the cache anymore, so the only place to get it is the database. This problem is called <a href="https://en.wikipedia.org/wiki/Cache_stampede">cache stampede</a>.</p> <p><img src="./statics/cached-access-expired.svg" alt="Cached data access with an empty cache"></p> <p>The database is not being shielded by an external cache layer anymore. It also does not have its own cache ready.</p> <h3>Access Locking</h3> <p>To prevent the same query being executed multiple times when a value is not cached, we can introduce a locking mechanism. Before asking the database, acquire a &quot;query lock&quot;.</p> <p>Our lock will be implemented using a cache service. In addition to <code>Get</code> and <code>Set</code> operations, <code>SetNx</code> is required.</p> <pre><code class="language-go">type CacheStore interface { Get(ctx context.Context, key string, destValue interface{}) error Set(ctx context.Context, key string, value interface{}, ttl time.Duration) error // SetNx sets the value of a given key only if it does not exist. // Returns ErrConflict if the key is already in use. SetNx(ctx context.Context, key string, value interface{}, ttl time.Duration) error } </code></pre> <p>When <code>FindItem</code> is called, we first try to read the item from the cache. If the item does not exist in the cache, we either acquire a lock and get data from the database or keep checking the cache. A value will be cached by another client or we will get the lock.</p> <pre><code class="language-go">func (c *cachedItemStore) FindItem(ctx context.Context, itemID int64) (*Item, error) { cacheKey := fmt.Sprintf(&quot;item:%d&quot;, itemID) readFromCache: for { var item Item switch err := c.cache.Get(ctx, cacheKey, &amp;item); err { case nil: return &amp;item, nil case ErrMiss: // Not in cache, fetch from the database, but only of // no other client is already doing this. cacheKeyLock := cacheKey + &quot;:query-lock&quot; switch err := c.cache.SetNx(ctx, cacheKeyLock, 1, time.Second); err { case nil: // We own the lock, ask the database about the value. break readFromCache case ErrConflict: // Another process owns the lock. Wait until // the value is stored in the cache or the lock // is released and we can query the database. // // Short sleep ensures that we do not overuse // the cache. time.Sleep(25 * time.Millisecond) continue readFromCache default: log.Printf(&quot;cannot acquire lock in cache: %s&quot;, err) break readFromCache } default: // Cache error is not critical for providing this // functionality. Log it and continue. log.Printf(&quot;cannot get from cache: %s&quot;, err) break readFromCache } } item, err := c.store.FindItem(ctx, itemID) if err == nil { if err := c.cache.Set(ctx, cacheKey, &amp;item, time.Minute); err != nil { log.Printf(&quot;cannot set in cache: %s&quot;, err) } } return item, err } </code></pre> <p>Above implementation ensures that at most one client is asking the database about an item with the same ID. If more than one <code>FindItem</code> call is done at the same time, only one client will query the database while all others are waiting for the cached result.</p> <h3>Early expiration</h3> <p>The situation has improved for the database. But adding locking means that when a value expires from the cache, all clients must wait until one of them fills the cache. All clients waste time and server resources on waiting.</p> <p>Our cache layer can be further improved by adding an early expiration functionality. If an <em>item</em> is cached for 1 minute, shortly before the expiration time is due, tell one of the clients that the value must be updated.</p> <pre><code class="language-go">func (c *cachedItemStore) FindItem(ctx context.Context, itemID int64) (*Item, error) { cacheKey := fmt.Sprintf(&quot;item:%d&quot;, itemID) cacheKeyLock := cacheKey + &quot;:query-lock&quot; readFromCache: for { var spItem stampedeProtectedItem switch err := c.cache.Get(ctx, cacheKey, &amp;spItem); err { case nil: // If an early expiration time is due, acquire lock to // fetch item from the database. if spItem.refreshAt.Before(time.Now()) { if c.cache.SetNx(ctx, cacheKeyLock, 1, time.Second) == nil { break readFromCache } // If we did not get the lock, we can still // return the cached data. It will expire soon, // but it's still valid. } return &amp;spItem.item, nil case ErrMiss: // Not in cache, fetch from the database, but only of // no other client is already doing this. switch err := c.cache.SetNx(ctx, cacheKeyLock, 1, time.Second); err { case nil: // We own the lock, ask the database about the value break readFromCache case ErrConflict: // Another process owns the lock. Wait until // the value is stored in the cache or the lock // is released and we can query the database. // // Short sleep ensures that we do not overuse // the cache. time.Sleep(25 * time.Millisecond) continue readFromCache default: log.Printf(&quot;cannot acquire lock in cache: %s&quot;, err) break readFromCache } default: log.Printf(&quot;cannot get from cache: %s&quot;, err) break readFromCache } } item, err := c.store.FindItem(ctx, itemID) if err == nil { spItem := stampedeProtectedItem{ refreshAt: time.Now().Add(55 * time.Second), item: item, } if err := c.cache.Set(ctx, cacheKey, &amp;spItem, time.Minute); err != nil { log.Printf(&quot;cannot set in cache: %s&quot;, err) } } return item, err } type stampedeProtectedItem struct { refreshAt time.Time item *Item } </code></pre> <h2>Conclusion</h2> <p>Caching data that is often read can increase performance of an application. Data caching is more complicated than it might look like at first sight.</p> <p>Above cache stampede protection code example is tightly coupled to <code>ItemStore</code>.</p> Accessing data in go. Piotr Husiatyński https://ph.codeberg.page/2018-09-02-accessing_data_in_go 2018-09-02T00:00:00Z <p>When writing a web application, we have to decide how to access data. Where to get it from, how to store it, how to manipulate it. Storage engines can vary, from being a single SQLite file to cache server or even an external service exposing an API.</p> <p>There are many ways this topic can be addressed. I will explain how a simple and straightforward solution can be evolved into a more sophisticated one.</p> <p>For the purpose of this article, let's assume that our storage engine is an SQL database with an <code>items</code> table. Our task is to build an endpoint, which returns a list of all <em>items</em> in the database. <em>Item</em> is an entity with a name and an ID. It can be represented by the structure below.</p> <pre><code class="language-go">type Item struct { ID int64 Name string } </code></pre> <h2>First iteration</h2> <p>Let's start with a basic HTTP handler. To avoid global variables, let's use dependency injection. <code>ItemListHandler</code> takes as a parameter what's necessary for the endpoint to complete our task -- a database connection and a template. In return we are getting an HTTP handler function.</p> <pre><code class="language-go">func ItemListHandler( db *sql.DB, tmpl *template.Template, ) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { // handler's code below } } </code></pre> <p>To list all <em>items</em>, we must first query the database. Once we will read all returned rows, we can use the collected entries to render the template and send the result back.</p> <pre><code class="language-go">rows, err := db.QueryContext(r.Context(), `SELECT id, name FROM items`) if err != nil { http.Error(w, &quot;Server Error&quot;, http.StatusInternalServerError) return } defer rows.Close() var items []*Item for rows.Next() { var it Item if err := rows.Scan(&amp;it.ID, &amp;it.Name); err != nil { http.Error(w, &quot;Server Error&quot;, http.StatusInternalServerError) return } items = append(items, &amp;it) } if err := rows.Err(); err != nil { http.Error(w, &quot;Server Error&quot;, http.StatusInternalServerError) return } _ = tmpl.Execute(w, items) </code></pre> <p><em>(To simplify the example, returned error pages are very basic, we do not log errors and we are assuming that template rendering never fails.)</em></p> <p>There are many issues with the approach presented above.</p> <ol> <li> <p>Every time we want to get the list of <em>items</em>, we must directly interact with the database. We must know about the database structure and in case of schema changes, we must locate all those places and update them.</p> </li> <li> <p>Everything is implemented in a single place. Because we directly access the database, to test this code, a database must be available, it's schema prepared and test data inserted.</p> </li> <li> <p>If we wanted to add a cache layer or some form of monitoring like tracing or metrics, we would have to add more code directly inside of the handler. That makes the code of the handler larger and testing harder. We can no longer test functionalities separately.</p> </li> </ol> <h2>Second iteration</h2> <p>Instead of writing all the code in an HTTP handler, let's extract a part of it as a function. We can encapsulate fetching items and hide the database connection from the user.</p> <p>The same code that was written directly inside of the handler is now provided by the <code>ListItems</code> method.</p> <pre><code class="language-go">// NewItemStore returns a store for items. func NewItemStore(db *sql.DB) *ItemStore { return &amp;ItemStore{db: db} } type ItemStore struct { db *sql.DB } // ListItems returns all stored items. func (is *ItemStore) ListItems(ctx context.Context) ([]*Item, error) { rows, err := db.QueryContext(ctx, `SELECT id, name FROM items`) if err != nil { return nil, fmt.Errorf(&quot;cannot select items: %s&quot;, err) } defer rows.Close() var items []*Item for rows.Next() { var it Item if err := rows.Scan(&amp;it.ID, &amp;it.Name); err != nil { return nil, fmt.Errorf(&quot;cannot scan item: %s&quot;, err) } items = append(items, &amp;it) } if err := rows.Err(); err != nil { return nil, fmt.Errorf(&quot;scanner: %s&quot;, err) } return items, nil } </code></pre> <p>Having such a <em>store</em> available, we no longer have to directly query the database in our handler. Instead of accepting <code>*sql.DB</code> as an argument, <code>ItemListHandler</code> can now take <code>*ItemStore</code>. Handler's body can be simplified to just a few lines.</p> <pre><code class="language-go">func ItemListHandler( itemStore *ItemStore, tmpl *template.Template, ) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { items, err := itemStore.ListItems(r.Context()) if err != nil { http.Error(w, &quot;Server Error&quot;, http.StatusInternalServerError) } _ = tmpl.Execute(w, items) } } </code></pre> <p>Having this handler, we no longer have to track changes to the database schema. All details of accessing <em>item</em> data are now in <code>ItemStore</code>. If you need to create or update an <em>item</em>, add <code>CreateItem</code> and <code>UpdateItem</code> methods.</p> <h2>Third iteration</h2> <p>Using <code>*ItemStore</code> for accessing <em>items</em> solved the first issue. Listing items is now an easy task that takes only a few lines of code.</p> <p>The last change is to use an interface instead of accepting a structure pointer. Let's call our interface <code>ItemStore</code>. The previous implementation using an SQL database is renamed to <code>sqlItemStore</code>.</p> <pre><code class="language-go">type ItemStore interface { ListItems(context.Context) ([]*Item, error) } // NewItemStore returns a store for items that is using an SQL database // as a storage engine. func NewSQLItemStore(db *sql.DB) ItemStore { return &amp;sqlItemStore{db: db} } type sqlItemStore struct { db *sql.DB } func (s *sqlItemStore) ListItems(ctx context.Context) ([]*Item, error) { // ... } func ItemListHandler( itemStore ItemStore, tmpl *template.Template, ) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { // ... } } </code></pre> <p>Defining interfaces together with the implementation might feel counterintuitive in Go. In most cases, it is better to declare an interface where it is used (not where it is implemented) to help to decouple functionalities and avoid dependencies.</p> <p>In this case we do not use an interface to encourage different <code>ItemStore</code> implementations. Code that is used for accessing <em>items</em> could be put in it's own package and provide all necessary functionality -- an interface, the main implementation using an SQL database, a mock implementation for testing and more.</p> <h3>Mocking for tests</h3> <p>The <code>sqlItemStore</code> implementation is easy to test independently from any HTTP handler that is using it. Any handler that is using an <code>ItemStore</code> should also be testable without the need for any particular <code>ItemStore</code> implementation.</p> <p>When testing handlers, instead of providing a real <code>ItemStore</code> implementation, we can use a mock.</p> <pre><code class="language-go">type ItemStoreMock struct { Items []*Item Err error } // ensure mock always implements the ItemStore var _ ItemStore = (*ItemStoreMock)(nil) func (mock *ItemStoreMock) ListItems(context.Context) ([]*Item, error) { return mock.Items, mock.Err } </code></pre> <p><code>ItemStoreMock</code> gives us full control over its API. We control what each method returns, which means we are able to test all cases we want.</p> <h3>Caching</h3> <p>Using an interface, allows us to wrap a store with additional functionality. For example, we can provide a cache layer, that will be invisible to the user. It can be added or removed without any changes to handler or store implementations.</p> <pre><code class="language-go">type CacheStore interface { // Get loads value under given key into destValue. ErrMiss is returned // if key does not exist. Get(ctx context.Context, key string, destValue interface{}) error // Set value of given key. Set(ctx context.Context, key string, value interface{}, ttl time.Duration) error } func CacheItemStore(cache CacheStore, store ItemStore) ItemStore { return &amp;cachedItemStore{ cache: cache, store: store, ttl: 5 * time.Minute, } } type cachedItemStore struct { cache CacheStore store ItemStore ttl time.Duration } func (c *cachedItemStore) ListItems(context.Context) ([]*Item, error) { var items []*Item switch err := c.cache.Get(ctx, &quot;items:all&quot;, &amp;items); err { case nil: return items, nil case ErrMiss: // all good, just not in the cache default: // log the error and continue } items, err := c.store.ListItems(ctx) if err == nil { if err := c.cache.Set(ctx, &quot;items:all&quot;, items, c.ttl); err != nil { // log the error and continue } } return items, err } </code></pre> <p>Testing of the <code>cachedItemStore</code> can be done using <code>ItemStoreMock</code> and an in-memory cache backend.</p> <h2>Conclusion</h2> <p>Writing data managers requires more effort, but allows to separate business logic from storage implementation. Separation of concerns gives us more control over data.</p> <p>Thanks to using Go interfaces, we can mock and extend functionality of the storage implementation. Integration with cache or monitoring tools is easy, pluggable and can be tested separately.</p>