October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsPC HealthRecommendedCrashes, freezes, slowdowns? Check your PC nowSpot repairable issues before they interrupt work.Check PCOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
World desk6 min

Why I Built Another In-Memory Cache for Go: The pacecache Trade-offs

pacecache is a bounded, in-process Go cache built to make caching trade-offs explicit. Here is what its entry-based capacity, segmentation, TTL cleanup, and stale-load rules mean in practice.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

pacecache is a generic, bounded, in-process cache for Go. Its author did not set out to build a cache that beats every existing Go library. The goal was to make the trade-offs of local caching explicit and tunable: lock contention, eviction quality, capacity use, expiration, memory overhead, and implementation complexity. Several behaviors decide whether it fits a given service. Capacity counts entries, not bytes. Segments split locks but also split capacity. Expiry is enforced on lookup before any memory is reclaimed. Coalescing duplicate loads does not by itself stop a stale result from being stored. And every process keeps its own cache.

What pacecache is, and what it is not

pacecache lives inside one Go process. Each process owns its cache state. The library does not provide shared cross-process state, persistence, centralized invalidation, or distributed consistency. If three replicas of a service each cache a product record, each replica holds its own copy and can disagree with the others until their entries expire or are removed.

As an Amazon Associate I earn from qualifying purchases.

The author frames this as a deliberate scope. The project is a free, MIT-licensed Go module with examples and documentation in its repository, and it installs through the standard Go module workflow. No separate server or infrastructure is required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Capacity is an entry budget, not a memory limit

The default capacity is up to 10,000 entries, one storage segment, and no time-based expiration, according to the author’s article (2026). The limit counts entries. It does not count bytes.

That distinction matters for memory planning. A cache holding 10,000 small integers and a cache holding 10,000 large JSON documents both meet the same entry budget, but their heap use can differ by orders of magnitude. To size a cache, measure the live heap with your real value types and multiply by the number of entries you expect to hold. Do not treat the entry count as a ceiling on process memory.

Segmentation: less lock contention, less flexible capacity

Each segment owns its storage, an LRU list, an expiration index, and a lock. Splitting the cache into more segments lets unrelated keys take different locks, which can reduce contention under concurrent load. The total capacity is then divided among the segments.

That division is the trade-off. If a few keys dominate traffic and hash into the same segment, that segment can start evicting while other segments still have free space. The author writes: “That makes segmentation a trade-off rather than a free performance switch.” The default of one segment avoids making that choice before the workload is known.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The author’s guidance on picking a value is direct: “The right segment count depends on the workload. It’s something worth measuring rather than guessing.”

Configuration values from the author’s article

Setting Default Illustrative configuration in the article
Capacity Up to 10,000 entries 100,000 entries, divided among segments
Segments 1 64
TTL None (no time-based expiration) 5 minutes
Jitter Not stated 30 seconds

The illustrative column shows how the settings can be combined. It is not a recommended production profile, and the article does not report measured results for it.

Expiration: logical validity first, physical cleanup second

An entry can be expired before its memory is actually freed. The author treats these as separate events: “An entry being expired is not the same thing as that entry already being physically removed from storage.”

In practice, a lookup that finds an expired entry treats it as a miss and removes it. Entries that are never read again are reclaimed through one of two paths: an explicit cleanup call, or optional background cleanup. The author prefers that split: “scheduling cleanup and enforcing expiration are two different concerns.” Enforcement does not depend on a background goroutine running, so TTL correctness holds even when cleanup is disabled. Cleanup only controls how quickly memory for untouched expired entries comes back.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Jitter

Jitter adds a random duration below a configured limit when an expiring entry is stored. Entries written in the same burst would otherwise expire together, causing a synchronized wave of misses and reloads. Spreading the deadlines reduces that alignment. The article gives 30 seconds of jitter on a 5-minute TTL as an example.

Sliding expiration

With sliding expiration enabled, a successful read extends the entry’s life using the effective TTL already chosen for that entry. Frequently read entries stay resident. Entries that are no longer read still expire on their normal schedule. Sliding expiration is optional; the article describes it alongside default and per-entry TTL choices.

Cache-aside loading and stale publication

GetOrLoadFunc accepts a loader for each call. Its behavior has three parts that are easy to conflate.

Coalescing duplicate misses

When several goroutines miss on the same key at once, they share one loader execution. Different keys load independently. For a database-backed lookup, this turns a burst of identical queries into one query.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Only successful, found results are cached. Not-found results and loader errors are not cached, so a missing record does not stay missing for the full TTL. Each waiting caller keeps its own context, so one caller can stop waiting without canceling the load for the others.

Coalescing does not prevent stale writes

Sharing a loader prevents duplicate work. It does not decide which value wins when a write happens during the load. Imagine this sequence: a load starts reading a row, a Set stores a newer value for the same key, and then the load finishes with the older row.

pacecache places publication barriers around mutations, including Set, GetOrSet, Delete, and Clear. If a mutation lands while a successful load is in flight, the load’s result is discarded and the call returns ErrLoadSuperseded. If the loader itself fails, its error takes precedence over the supersession outcome. The project README describes the same rule: newer mutations take precedence over stale loaded results.

Callers that receive ErrLoadSuperseded should treat it as “the cache now holds something newer,” not as a cache failure. Retrying the read will usually return the newer value.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Stats and observability

Stats() returns a detached snapshot of cache state and activity. Reads across independent segments are not one globally atomic instant, so counters from different segments may reflect slightly different moments. For dashboards and trend lines this is usually acceptable. For exact accounting during a test, read the numbers while the cache is quiet.

OpenTelemetry integration is available through the extra/paceotel package. The application remains responsible for setting up the OpenTelemetry SDK, its lifecycle, and its exporters.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

How to benchmark it against your alternatives

The author separates three questions, and each needs its own test:

  • Concurrent throughput, measured with multiple workers hitting the cache at once. The project README’s throughput configuration uses 8 workers.
  • Hit ratio under a skewed access pattern, where a small set of keys receives most requests. The README’s configuration uses 1,000,000 requests.
  • Live heap after populating fixed-size keys and values. The README’s memory configuration uses fixed 32-byte keys and values.

The README reports the benchmark hardware as an Intel Core i7-12700H with 14 cores and 20 threads. These are test settings. The material available for this article does not publish result figures, so it does not establish that pacecache is faster, more memory-efficient, or has a higher hit ratio than any other Go cache. Run the three tests on your own key distribution, value sizes, and concurrency before choosing.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

When an in-process cache fits, and when it does not

The author’s guidance points to an in-process cache when:

  • The data is safe to hold locally for the TTL you choose.
  • Avoiding a network hop to a cache server matters for latency.
  • The upstream lookup is expensive enough that cache-aside loading pays off.
  • Each instance holding its own, possibly different, cache contents is acceptable.
  • You want a bounded local hot set rather than an unbounded map.

If several service instances must share one coordinated cache, the author points to Redis or another distributed system. Those tools solve a different problem. pacecache is not a drop-in distributed cache, and it will not synchronize invalidations across replicas.

Source and evidence limits

The main account is the pacecache author’s first-person design explanation, published in 2026 and syndicated on Dev.to. The project’s GitHub README supports several API and concurrency behaviors, including lazy expiration, optional cleanup, and newer-mutation precedence. Neither source establishes independent production adoption, and neither presents a comparative benchmark result against other Go libraries. Treat the design claims as the author’s description of the library, and verify behavior that matters to your system with your own tests.

The author states the central intent plainly: “I wasn’t trying to build a cache that would be universally better than every existing alternative.” Read the library in that spirit: as a set of explicit choices, each with a cost you can measure.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

“

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
Outdated Drivers Are Slowing You DownFree scan - exact matches
PC Slower Than It Used to Be?Free scan - under a minute

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.