refresh

package
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 21, 2026 License: Apache-2.0 Imports: 3 Imported by: 0

Documentation

Overview

Package refresh defines how stale-while-revalidate revalidation is scheduled.

Two strategies exist because of Cloud Run's CPU model. Request-bound refresh uses a short-lived detached goroutine and no worker pool: it makes progress whenever the instance has CPU (i.e. while it is processing requests), which is the best you can do under request-only CPU allocation. Background refresh uses a long-lived worker pool and therefore requires always-on CPU. Neither guarantees completion while the instance is idle; the next request re-triggers a still-stale entry.

Keys are strings, matching the rest of nimbus's internal key space.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Background

type Background struct {
	// contains filtered or unexported fields
}

Background revalidates stale entries on a fixed worker pool fed by a bounded queue.

WARNING: on Cloud Run this REQUIRES always-on CPU (instance-based billing / --no-cpu-throttling). With the default request-only allocation, worker goroutines are throttled to near-zero between requests and refreshes stall. Prefer RequestBound unless your service already runs always-on CPU.

func NewBackground

func NewBackground(workers, queueSize int, timeout time.Duration) *Background

NewBackground starts a pool of workers draining a queue of depth queueSize. timeout bounds each revalidation; <= 0 means no timeout.

func (*Background) Close

func (b *Background) Close() error

Close stops accepting work, drains in-flight jobs, and waits for workers.

func (*Background) Schedule

func (b *Background) Schedule(key string, run func(ctx context.Context) error) bool

Schedule enqueues run for key unless one is in flight or the queue is full (in which case the refresh is dropped and the entry stays stale until the next trigger). It returns true only when it actually enqueues a refresh.

type Refresher

type Refresher interface {
	// Schedule requests a revalidation of key, performed by run. It returns true
	// if it actually launched a refresh, and false if it was suppressed (a
	// refresh for key was already in flight, the refresher is closed, or the
	// queue was full), so callers can count real launches.
	Schedule(key string, run func(ctx context.Context) error) bool
	// Close stops any in-flight or pending refreshes and releases resources.
	Close() error
}

Refresher schedules revalidation of cache keys. Implementations dedupe per key and Schedule is non-blocking.

type RequestBound

type RequestBound struct {
	// contains filtered or unexported fields
}

RequestBound revalidates stale entries on an ad-hoc detached goroutine; it keeps no long-lived worker pool.

On Cloud Run with request-only CPU allocation this is the safe default, but note the honest semantics: the refresh goroutine is detached (it does not share the triggering request's context), and Cloud Run allocates CPU per instance only while that instance is processing requests. So the refresh makes progress whenever the instance is handling any request and may stall while the instance is idle; the next request re-triggers a still-stale entry. It is best-effort, bounded by RefreshTimeout, not a guarantee of completion.

Fan-out is capped: at most maxConcurrent revalidations run at once. Without a cap, a synchronized stale wave (a deploy or a cold autoscaled fleet expiring many keys at once) would spawn one goroutine and one loader call per distinct key — a thundering herd on the origin precisely when it is most fragile. On saturation a Schedule is dropped (the entry stays stale and re-triggers on the next request), mirroring Background's queue-full drop. Stale-while-revalidate keeps serving the stale value meanwhile, so a conservative cap only slows how fast the stale set drains; it never fails a read.

func NewRequestBound

func NewRequestBound(timeout time.Duration, maxConcurrent int) *RequestBound

NewRequestBound returns a request-bound refresher. timeout bounds each revalidation (<= 0 means no timeout). maxConcurrent caps concurrent revalidations; <= 0 selects defaultMaxConcurrentRefresh.

func (*RequestBound) Close

func (r *RequestBound) Close() error

Close prevents new refreshes and waits for in-flight ones to finish. Unlike Background.Close it needs no idempotency guard: it closes no channel, so a repeat call merely re-sets closed and re-Waits on an already-drained group.

func (*RequestBound) Schedule

func (r *RequestBound) Schedule(key string, run func(ctx context.Context) error) bool

Schedule launches run for key unless a refresh for key is already in flight, the refresher is closed, or the concurrency cap is saturated. It returns true only when it actually launches.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL