Library / SDK
dgraph-io/ristretto avatar
dgraph-io/ristretto

Ristretto: Dgraph's memory-bound Go cache library

A high performance memory-bound Go cache

6,995 stars450 forksGoApache-2.0

At a glance

What is it?
Ristretto is a concurrent in-process cache for Go built around TinyLFU admission and SampledLFU eviction. It trades some Set calls for throughput and hit ratio, and it is not a distributed cache.
Who is it for?
Adopt Ristretto if you are writing Go and need a single-process cache with high hit ratios under contention, and you can accept that a Set may be dropped before it reaches the store. Do not adopt it if you need a cache shared across processes or machines, or if you cannot tolerate a Get that briefly misses a value you just wrote.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 9 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The problem Ristretto solves: contention in a shared in-process cache

The README states the motivation directly: the project was built out of a need for a contention-free cache in Dgraph. That is a narrower goal than "a fast cache". A cache that sits behind many goroutines on the same process has to answer two questions at once, and the usual answer to one damages the other. A single mutex-protected map gives correct reads but serialises every lookup. Sharding the map reduces lock contention but makes eviction a per-shard decision, so a shard can evict an item that the whole cache would have kept.

Ristretto's audience is the Go developer embedding a cache inside a service, a database, or a storage engine. Badger and Dgraph are listed as known users, and both are single-process components where a cache lookup happens on a hot path. The library is a Go module you import, not a server you run. The README answers the distribution question plainly: "No, it's just like any other Go library that you can import into your project and use in a single process." If your cache needs to be shared across machines, this project is the wrong layer, and no amount of configuration changes that.

TinyLFU admission and SampledLFU eviction, and why Set can be dropped

The two mechanisms named in the README are the admission policy and the eviction policy. Admission is TinyLFU, described as costing "12 bits per counter"; eviction is SampledLFU, which the README says is "on par with exact LRU and better performance on Search and Database traces". The split matters. Admission decides whether a newly written key is allowed into the cache at all, using a frequency estimate of how often keys have been seen. Eviction decides which existing key leaves when the cost budget is full.

Cost is a first-class concept. The Config takes MaxCost, and every Set call passes a cost value, which the README describes as possibly "anything". A large new item can evict several smaller ones if the admission policy judges it valuable. That is a different model from a cache that counts entries, and it means your cost function is part of your cache's behaviour, not a detail.

The price is eventual consistency in the write path. The README's own FAQ says the only shortcut is "dropping some Set calls": a Set for a new item is not guaranteed to enter the cache, and it can be dropped either in the Set buffer or at the admission step. Updates are guaranteed. The README's argument is that popular items get Set repeatedly and eventually land. The repository layout matches that story: cache.go, policy.go, ring.go, sketch.go, store.go and ttl.go are separate files, so the admission sketch, the eviction policy, the ring buffer and the value store are distinct components rather than one locked map.

Installing Ristretto and a first cache with the v2 generics API

The README requires Go 1.21 or above and go modules. From your project directory, fetch the library:

bash
go get github.com/dgraph-io/ristretto/v2

On version choice, the README is explicit: v1.x.x is the first version used in most programs with Ristretto dependencies, while v2.x.x adds generics and "a slightly different interface", and new programs are advised to use v2. Note that the module's go.mod declares go 1.24.0 with toolchain go1.25.0, which is a higher floor than the README's stated 1.21. If your build environment pins an older toolchain, that difference will surface at build time.

The README gives this example, which is the shortest path to a working cache. It sets NumCounters to 1e7, MaxCost to 1 << 30, and BufferItems to 64, then writes, waits, reads and deletes:

go
package main

import (
  "fmt"

  "github.com/dgraph-io/ristretto/v2"
)

func main() {
  cache, err := ristretto.NewCache(&ristretto.Config[string, string]{
    NumCounters: 1e7,     // number of keys to track frequency of (10M).
    MaxCost:     1 << 30, // maximum cost of cache (1GB).
    BufferItems: 64,      // number of keys per Get buffer.
  })
  if err != nil {
    panic(err)
  }
  defer cache.Close()

  cache.Set("key", "value", 1)
  cache.Wait()

  value, found := cache.Get("key")
  if !found {
    panic("missing value")
  }
  fmt.Println(value)

  cache.Del("key")
}

Two details in that snippet are the ones people get wrong. First, the comment on cache.Wait() says it waits "for value to pass through buffers", and the example calls it between Set and Get. Second, Get returns two values and the example treats a false found as a panic, which is fine for a demo but not for production code. The README also notes that metrics are optional and can report throughput and hit ratios, though it does not document the metric names in the text available here.

Where Ristretto is the wrong tool

The dropped-Set behaviour is the limitation to take seriously. If your program writes a value and immediately reads it back expecting it to be there, Ristretto can return found as false, because the write may still be in the buffer or may have been rejected by admission. Calling Wait() after every Set removes that window, but Wait is a synchronisation point, and using it on every write gives back much of the throughput the buffering was there to provide. The right shape is a workload where a miss is cheap and a re-read is likely, not one where a miss is a correctness bug.

Memory bounding is another boundary. The name says memory-bound, and MaxCost is a budget, not a hard limit enforced by the runtime. If your cost function understates the real size of a value, the process can hold more than you planned. Cost is whatever you pass to Set, so the accuracy of that number is your responsibility.

The third case is distribution. The README answers the distributed question with a flat no. If two processes need to see the same cached entry, or if a cache must survive a process restart, this library does not do that, and adding it on top would mean writing the coordination layer yourself.

Ristretto compared with an exact LRU cache

The natural alternative is an exact LRU cache, the kind built from a map plus a doubly linked list under one lock. The difference is in what each one optimises. An exact LRU knows the true recency order of every key, so its eviction decision is precise, and its write path is immediate: a Set either inserts or it does not, and a following Get sees the result. Its cost is contention. Every read that updates recency touches the shared structure, and under many goroutines that structure becomes the bottleneck.

Ristretto accepts approximation to buy concurrency. SampledLFU samples rather than tracking the full order, and the README describes it as "on par with exact LRU and better performance on Search and Database traces". TinyLFU adds a frequency estimate at 12 bits per counter. The trade is visible in the API: Ristretto gives you Wait(), a function an exact LRU does not need, because Ristretto has buffers to drain. It also gives you cost-based eviction, where one large item can displace several small ones, which a count-based LRU cannot express without you faking it. Pick the exact LRU when writes must be immediately visible and the lock is not your bottleneck. Pick Ristretto when the read path is the bottleneck and eventual visibility of new writes is acceptable.

Maintenance, versioning and the Apache-2.0 licence

The repository is not archived, and the last push was on 2026-09-21, one day before this writing, so the project is under current development by the only measure available here. The release history shows v2.4.2 on 2026-07-07, v2.4.1 on 2026-07-06, and v2.4.0 on 2026-01-21. The gap between v2.4.0 in January and the v2.4.1 and v2.4.2 pair in July suggests patch releases cluster rather than arrive steadily, which is normal for a library whose interface is stable.

The upgrade cost is mostly the v1 to v2 step. The README says v2 has "a slightly different interface" because of generics, and that it exists to solve compatibility problems for programs on the old version. Within v2, the Config fields shown here (NumCounters, MaxCost, BufferItems) are the surface most callers touch. The module declares go 1.24.0 and a go1.25.0 toolchain, so moving to a new Ristretto minor can move your minimum Go version with it; check go.mod before upgrading in a pinned environment.

On licensing, Ristretto is Apache-2.0, and the repository carries a LICENSE file alongside a SECURITY.md and CONTRIBUTING.md. Apache-2.0 is a permissive licence with an explicit patent grant and notice requirements. This is a description of the licence identifier, not legal advice; if you redistribute the library or a derivative, read the LICENSE file and your own legal guidance.

Editorial conclusion

Adopt Ristretto if you are writing Go and need a single-process cache with high hit ratios under contention, and you can accept that a Set may be dropped before it reaches the store. Do not adopt it if you need a cache shared across processes or machines, or if you cannot tolerate a Get that briefly misses a value you just wrote. Before committing, verify three things in your own code: that you call cache.Wait() where the README says to wait for buffered writes, that your NumCounters and MaxCost values match the working set you actually have, and that your callers handle a false return from Get as a normal outcome rather than an error.

Frequently asked questions

Is Ristretto a distributed cache?

No. The README states it is like any other Go library you import into your project and use in a single process.

How do I install Ristretto in a Go module?

The README says to install Go 1.21 or above and run go get github.com/dgraph-io/ristretto/v2 from your project. The module's go.mod declares go 1.24.0 with toolchain go1.25.0.

Does Ristretto guarantee that every Set is stored?

No. The README FAQ says a Set for a new item is not guaranteed to make it into the cache, because it can be dropped in the Set buffer or at the admission policy. Updates to existing items are guaranteed.

Should I use Ristretto v1 or v2?

The README says v2.x.x adds generics with a slightly different interface, and that new programs are recommended to use it. v1.x.x is described as the first version used in most programs with Ristretto dependencies.

Which admission and eviction policies does Ristretto use?

Admission is TinyLFU, which the README says costs 12 bits per counter, and eviction is SampledLFU, which it describes as on par with exact LRU and better on Search and Database traces.

Official sources

  1. dgraph-io/ristretto on GitHub
  2. License: Apache-2.0
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/dgraph-io-ristretto.svg)](https://hysenlabs.com/projects/dgraph-io-ristretto)