Stop Fetching the Same Data in Go: Cache It Instead!
Rijul Rajesh

Rijul Rajesh @rijultp

About: Student exploring various technologies

Joined:
Nov 12, 2023

Stop Fetching the Same Data in Go: Cache It Instead!

Publish Date: Apr 4
13 3

If you’ve been working with Go and have ever thought:

“Why am I fetching this value again? It hasn’t changed!”

Then let me introduce you to one of the cleanest little gems in the standard library: sync.Once.

This article is all about using sync.Once to cache values that you fetch frequently but that never really change after the first time.

The Problem

Sometimes, you need to fetch a value that is expensive to retrieve:

  • A config value from your database
  • A list of supported languages
  • A long-lived token from an API

But once you have it… you really don’t need to fetch it again.

Fetching it over and over? Wasteful.

Fetching it once and reusing it? Beautiful.

The Go Way: sync.Once

The sync.Once type ensures that a piece of code runs exactly one time, no matter how many goroutines try to call it.

That’s perfect for lazy-loaded, one-time cache situations.

Here’s what it looks like in practice:

package main

import (
    "fmt"
    "sync"
    "time"
)

var (
    configValue string
    once        sync.Once
)

func fetchConfigFromDB() string {
    fmt.Println("Fetching config from DB...")
    time.Sleep(2 * time.Second) // Simulate delay
    return "my-config-value"
}

func GetConfig() string {
    once.Do(func() {
        configValue = fetchConfigFromDB()
    })
    return configValue
}

func main() {
    fmt.Println("First call:", GetConfig())
    fmt.Println("Second call:", GetConfig())
    fmt.Println("Third call:", GetConfig())
}
Enter fullscreen mode Exit fullscreen mode

Output:

Fetching config from DB...
First call: my-config-value
Second call: my-config-value
Third call: my-config-value
Enter fullscreen mode Exit fullscreen mode

Notice how "Fetching config from DB..." only prints once?

That’s the magic of sync.Once.


When to Use This

This approach is great when:

  • The value doesn’t change after the first load
  • You want lazy loading (don’t fetch at startup)
  • Thread safety matters (it’s built-in)

Examples:

  • First-time loading of environment-based config
  • Reading a file only once
  • Caching an API key/token that’s valid for a long time

When Not to Use It

sync.Once is not for values that:

  • Change over time
  • Need to be refreshed
  • Depend on request-specific data

For those cases, consider a cache with expiry, or something more dynamic.


Final Thoughts

sync.Once is one of those tools that’s incredibly simple but solves a real problem in a clean way. If you ever find yourself writing something like:

var loaded = false
var mu sync.Mutex

func Load() {
    mu.Lock()
    defer mu.Unlock()
    if !loaded {
        // do something once
        loaded = true
    }
}
Enter fullscreen mode Exit fullscreen mode

…you probably just want sync.Once.

If you're a software developer who enjoys exploring different technologies and techniques like this one, check out LiveAPI. It’s a super-convenient tool that lets you generate interactive API docs instantly.

So, if you’re working with a codebase that lacks documentation, just use LiveAPI to generate it and save time!

You can instantly try it out here! 🚀

Comments 3 total

  • Nesniv Ogem
    Nesniv OgemApr 5, 2025

    Examples:

    • First-time loading of environment-based config
    • Reading a file only once
    • Caching an API key/token that’s valid for a long time

    Any reason why not load the config or read the file once on start up?
    Caching an API key/token that’s valid for a long time isn't it risky if it's not valid permanently?
    Any other example when we can use sync.Once?

    • Aashish Koshti
      Aashish KoshtiApr 6, 2025

      the examples mentioned like loading config file etc are more centric towards singleton design pattern. sync.Once helps you to do that. any place where you need to initialise a class once it can be used. more examples in which i personally use sync.Once are initialising db conn, repository class, env config, struct validators, shared worker pool, and many more

  • Vineet Salve
    Vineet SalveApr 6, 2025

    I am a complete beginner so this might look like a stupid question..... Is it okay to cache the .env varibles?

Add comment