ubgo is in early alpha: libraries and APIs may still change.

Skip to content
ubgo Alpha

Refresh-ahead and stale values in Remember

Follow one cache entry through its life and see what Remember returns, and what it refreshes in the background, at each point.

Based on the cache README, section “Remember and all its options” About cache →

Step 1 of 7

The first call loads and stores

The README example uses a 10 minute TTL. On a miss the loader runs, and the entry is stored with a backend TTL of 12 minutes: the soft TTL plus the widest stale window (2 minutes).

All steps as text
  1. The first call loads and stores. The README example uses a 10 minute TTL. On a miss the loader runs, and the entry is stored with a backend TTL of 12 minutes: the soft TTL plus the widest stale window (2 minutes).
  2. Fresh: a plain cache hit. Before refresh-ahead starts, a call just returns the cached value. Nothing else happens.
  3. Refresh-ahead: refresh before expiry. WithRefreshAhead(0.8) means: once 80% of the TTL has passed (8 minutes here), refresh in the background and return the still-valid value at once. A hot key never expires under load.
  4. Stale-while-revalidate. After the 10 minute expiry, WithStaleWhileRevalidate(30s) keeps serving the stale value for 30 seconds while one background load refreshes it.
  5. Past that window, the loader runs. After the stale-while-revalidate window, the call goes to the single-flight loader like a miss. If the loader works, the caller gets the new value.
  6. Stale-if-error: availability first. If that loader call fails, WithStaleIfError(2m) returns the stale value instead of the error, as long as it is within 2 minutes past expiry. This trades staleness for availability.
  7. After 12 minutes the entry is gone. The backend dropped the entry at its 12 minute TTL. A call now is a plain miss, and if the loader fails, the caller gets the error.

The options from the README, all on one call:

p, err := cache.Remember(ctx, c, "product:99", 10*time.Minute,
    func(ctx context.Context) (Product, error) { return db.Product(ctx, 99) },
    cache.WithRefreshAhead(0.8),
    cache.WithStaleWhileRevalidate(30*time.Second),
    cache.WithStaleIfError(2*time.Minute),
    cache.WithNegativeTTL(15*time.Second),
    cache.WithJitter(0.1),
)

Remember stores values inside a small JSON envelope (born, soft expiry, missing) so the staleness data survives any value codec. The diagram leaves out WithNegativeTTL, which caches a loader’s ErrNotFound for a while, and WithJitter, which adds ±10% random noise to the stored TTL so keys written together do not all expire at once.