daramjwee

package module
v0.9.1 Latest Latest
Warning

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

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

README

daramjwee 🐿️ /dɑːrɑːmdʒwiː/

A pragmatic and lightweight hybrid caching middleware for Go.

daramjwee sits between your application and your origin data source (e.g., a database or an API), providing an efficient, stream-oriented hybrid caching layer. It is designed with a focus on simplicity and core functionality to achieve high throughput at a low cost in cloud-native environments.

Core Design Philosophy

daramjwee is built on two primary principles:

  1. Stream-Oriented API: Public reads and writes are expressed through io.Reader and io.Writer interfaces, so store implementations can stream data without forcing full in-memory buffering in user code.

    Key semantics:

    • Callers must always Close() the returned stream to finalize the operation and release resources.
    • Tier-0 hits are returned directly as streams.
    • Lower-tier hits and origin misses are streamed to the caller while tier 0 is filled in the same read lifecycle.
    • If the caller stops early and closes the stream, the staged write is discarded instead of publishing partial data.
  2. Modular and Pluggable Architecture: Key components such as the storage backend (Store), eviction strategy (EvictionPolicy), and asynchronous task runner (Worker) are all designed as interfaces. This allows users to easily swap in their own implementations to fit specific needs.

Current Status & Key Implementations

daramjwee is more than a proof-of-concept; it is a stable and mature library ready for production use. Its robustness is verified by a comprehensive test suite, including unit, integration, and stress tests.

  • Robust Storage Backends (Store):

    • FileStore: Guarantees atomic writes by default using a "write-to-temp-then-rename" pattern to prevent data corruption. It also offers a copy-based alternative (WithCopyWrite) for compatibility with network filesystems, though this option is not atomic and may leave orphan files on failure, and it is not supported as a top-tier (tier 0) store due to limitations with the stream-through publish contract.
    • MemStore: A thread-safe, high-throughput in-memory store with fully integrated capacity-based eviction logic. Its performance is optimized using sync.Pool to reduce memory allocations under high concurrency.
    • objectstore: A first-party Store for thanos-io/objstore providers (S3, GCS, Azure Blob Storage). It is designed for cost-efficient durable caching, especially when object count and large-object read cost matter, while still fitting into the same ordered-tier cache model as the other backends. Its local dataDir is an ingest/catalog workspace, not a persistent local read-cache tier. If you want durable remote backing plus local file-cache hits, place FileStore ahead of objectstore in WithTiers(...). In distributed deployments, concurrent writes to the same key are still last-writer-wins unless you coordinate writers externally. Local emulator demos for the objectstore path now live under examples/file_objstore_gcs_vind and examples/file_objstore_s3_vind, while the older GCS examples remain real-cloud configuration examples.
  • Advanced Eviction Policies (EvictionPolicy):

    • In addition to the traditional LRU, it implements modern, high-performance algorithms like S3-FIFO and SIEVE, allowing you to choose the optimal policy for your workload.
  • Reliable Concurrency Management:

    • Worker Pool (Worker): A configurable worker pool manages background tasks like cache refreshes, preventing unbounded goroutine creation and ensuring stable resource usage under load.
    • Striped Locking (FileLockManager): FileStore uses striped locking instead of a single global lock, minimizing lock contention for different keys during concurrent requests.
  • Efficient Caching Logic:

    • CacheTag-based Optimization: The cache tracks a cache-owned validator (CacheTag) for each representation. Origins can reuse it for conditional fetches, and applications can map it to external HTTP ETag headers if desired.
    • Negative Caching: Caches the "not found" state for non-existent keys, preventing repeated, wasteful requests to the origin.
    • Stale-While-Revalidate: Can serve stale data while asynchronously refreshing it in the background, minimizing latency while maintaining data freshness. This replaces the previous "Grace Period" concept.

Data Retrieval Flow

The data retrieval process in daramjwee follows a clear, tiered approach to maximize performance and efficiency.

flowchart TD
    A[Client Request for a Key] --> B{Check Tier 0};

    B -- Hit --> C{Is item stale?};
    C -- No --> D[Stream data to Client];
    D --> E[End];
    C -- Yes --> F[Stream STALE data to Client];
    F --> G(Schedule Background Refresh);
    G --> E;

    B -- Miss --> H{Check Lower Tiers};

    H -- Hit --> I[Stream while filling Tier 0];
    I --> E;

    H -- Miss --> J[Fetch from Origin];
    J -- Success --> K[Stream from Origin while filling Tier 0];
    K --> L(Optionally: Schedule async fan-out to lower tiers);
    L --> E;
    
    J -- Not Found (Cacheable) --> M[Cache Negative Entry];
    M --> N[Return 'Not Found' to Client];
    N --> E;
    
    J -- Not Modified (304) --> O{Re-fetch from Tier 0};
    O -- Success --> D;
    O -- Failure (e.g., evicted) --> N;
  1. Check Tier 0: Looks for the object in the first regular tier.
    • Hit (Fresh): Immediately returns the object stream to the client.
    • Hit (Stale): Immediately returns the stale object stream to the client and schedules a background task to refresh the cache from the origin.
  2. Check lower tiers: If tier 0 misses, daramjwee checks the remaining ordered tiers.
    • Hit: Streams the object to the caller while simultaneously filling tier 0, so the next access is a tier-0 hit if the caller finishes and closes the stream.
  3. Fetch from Origin: If the object is in neither tier (Cache Miss), it invokes the user-provided Fetcher.
    • Success: The fetched data is streamed directly to the client while simultaneously filling tier 0. Once the read completes and the caller closes the stream, the entry becomes visible in tier 0 and can be fanned out asynchronously to lower tiers.
    • Not Modified: If the origin returns ErrNotModified, daramjwee attempts to re-serve the data from tier 0.
    • Not Found: If the origin returns ErrCacheableNotFound, a negative entry is stored to prevent repeated fetches.

Getting Started

Here is a simple example of using daramjwee in a web server.

package main

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/go-kit/log"
	"github.com/go-kit/log/level"
	"github.com/mrchypark/daramjwee"
	"github.com/mrchypark/daramjwee/pkg/store/filestore"
)

// 1. Define how to fetch data from your origin.
type originFetcher struct {
	key string
}

// A simple in-memory origin for demonstration.
var fakeOrigin = map[string]struct {
	data string
	etag string
}{
	"hello": {"Hello, Daramjwee! This is the first object.", "v1"},
	"world": {"World is beautiful. This is the second object.", "v2"},
}

func (f *originFetcher) Fetch(ctx context.Context, oldMetadata *daramjwee.Metadata) (*daramjwee.FetchResult, error) {
	oldCacheTag := "none"
	if oldMetadata != nil {
		oldCacheTag = oldMetadata.CacheTag
	}
	fmt.Printf("[Origin] Fetching key: %s (old CacheTag: %s)\n", f.key, oldCacheTag)

	// In a real application, this would be a DB query or an API call.
	obj, ok := fakeOrigin[f.key]
	if !ok {
		return nil, daramjwee.ErrCacheableNotFound
	}

	// If the CacheTag matches, notify that the content has not been modified.
	if oldMetadata != nil && oldMetadata.CacheTag == obj.etag {
		return nil, daramjwee.ErrNotModified
	}

	return &daramjwee.FetchResult{
		Body:     io.NopCloser(bytes.NewReader([]byte(obj.data))),
		Metadata: &daramjwee.Metadata{CacheTag: obj.etag},
	}, nil
}

func main() {
	logger := log.NewLogfmtLogger(os.Stderr)
	logger = level.NewFilter(logger, level.AllowDebug())

	// 2. Create the tier 0 store.
	tier0Store, err := filestore.New("./daramjwee-cache", log.With(logger, "tier", "0"))
	if err != nil {
		panic(err)
	}

	// 3. Create a daramjwee cache instance with ordered tiers.
	cache, err := daramjwee.New(
		logger,
		daramjwee.WithTiers(tier0Store),
		daramjwee.WithOpTimeout(5*time.Second),
		// This configures the default freshness for the ordered tier chain.
		daramjwee.WithFreshness(1*time.Minute, 30*time.Second),
		daramjwee.WithCloseTimeout(10*time.Second),
	)
	if err != nil {
		panic(err)
	}
	defer cache.Close()

	// 4. Use the cache in your HTTP handlers.
	http.HandleFunc("/objects/", func(w http.ResponseWriter, r *http.Request) {
		key := strings.TrimPrefix(r.URL.Path, "/objects/")

		// Call cache.Get() to retrieve the current cache decision.
		resp, err := cache.Get(r.Context(), key, daramjwee.GetRequest{
			IfNoneMatch: r.Header.Get("If-None-Match"),
		}, &originFetcher{key: key})
		if err != nil {
			if err == daramjwee.ErrCacheClosed {
				http.Error(w, "Server is shutting down", http.StatusServiceUnavailable)
			} else {
				http.Error(w, err.Error(), http.StatusInternalServerError)
			}
			return
		}
		defer resp.Close()

		if resp.Metadata.CacheTag != "" {
			w.Header().Set("ETag", resp.Metadata.CacheTag)
		}

		switch resp.Status {
		case daramjwee.GetStatusNotFound:
			http.Error(w, "Object Not Found", http.StatusNotFound)
			return
		case daramjwee.GetStatusNotModified:
			w.WriteHeader(http.StatusNotModified)
			return
		}

		// Stream the response directly to the client.
		io.Copy(w, resp)
	})

fmt.Println("Server is running on :8080")
http.ListenAndServe(":8080", nil)
}

Freshness Configuration

WithFreshness(...) defines the chain-wide default freshness policy for ordered tiers.

If a specific tier needs a different window, override just that tier index:

cache, err := daramjwee.New(
    logger,
    daramjwee.WithTiers(memTier, fileTier, objectTier),
    daramjwee.WithFreshness(30*time.Second, 5*time.Second),
    daramjwee.WithTierFreshness(1, 5*time.Minute, 5*time.Second),
    daramjwee.WithTierFreshness(2, 30*time.Second, time.Minute),
)

Tier indexes follow the order passed to WithTiers(...). Because WithTiers(...) replaces the whole chain, it is best to define the final tier list and any WithTierFreshness(...) overrides together in the same daramjwee.New(...) call.

Background refresh jobs use the "pool" worker strategy by default. If you need the old fire-one-goroutine-per-job behavior, set WithWorkerStrategy("all") explicitly. Supported strategies are "pool" and "all"; unknown strategy values now fail cache construction instead of silently falling back to "pool".

Cache Groups

New(...) constructs a self-contained cache instance with its own background runtime. That is the right choice when you want one cache to manage its own worker lifecycle, shutdown behavior, and queue limits independently.

NewGroup(...) constructs a CacheGroup that owns one bounded shared background runtime for multiple caches. Call group.NewCache(...) to create cache instances that share that runtime while keeping their tier chains and cache-local policy separate. Each grouped cache still closes independently, and group.Close() first shuts down the shared runtime under the group close timeout and then closes the created caches.

Group construction uses the WithGroup... option surface:

  • WithGroupWorkers(...)
  • WithGroupWorkerTimeout(...)
  • WithGroupWorkerQueueDefault(...)
  • WithGroupCloseTimeout(...)

Per-cache runtime tuning inside a group uses the regular cache options WithWeight(...) and WithQueueLimit(...). Those options only apply to caches created from a CacheGroup; standalone New(...) construction keeps using the original cache-level worker options such as WithWorkers(...), WithWorkerQueue(...), WithWorkerTimeout(...), and WithWorkerStrategy(...).

Minimal example:

group, err := daramjwee.NewGroup(
    logger,
    daramjwee.WithGroupWorkers(2),
    daramjwee.WithGroupWorkerQueueDefault(8),
)
if err != nil {
    panic(err)
}
users, err := group.NewCache(
    "users",
    daramjwee.WithTiers(memTier, fileTier),
    daramjwee.WithWeight(4),
    daramjwee.WithQueueLimit(16),
)
if err != nil {
    panic(err)
}
defer users.Close()
defer group.Close()

See examples/cache_group for a runnable local demo. See examples/file_objstore_gcs_vind and examples/file_objstore_s3_vind for runnable local objectstore demos on emulator-backed buckets.

objectstore Configuration

objectstore exposes its behavior entirely through constructor options. There are two separate configuration layers:

  • daramjwee.WithTiers(...) decides where objectstore sits in the ordered tier chain.
  • objectstore.With...(...) options tune how the backend stores and reads remote objects.

The most important design point is that objectstore.WithDir(...) is not a user-visible file-cache tier. It stores local catalog state and flush spool data so the backend can stream writes efficiently to remote object storage. If the pod restarts with an empty directory, already-flushed remote entries can still be served from the remote checkpoint/segment state. If you want local filesystem read-cache behavior after restart, put FileStore in front of objectstore.

Typical ordered-tier layout:

tier0, err := filestore.New("/var/lib/daramjwee/tier0", log.With(logger, "tier", "0"))
if err != nil {
    return err
}

cache, err := daramjwee.New(
    logger,
    daramjwee.WithTiers(
        tier0,
        objectstore.New(
            bucket,
            log.With(logger, "tier", "1"),
            objectstore.WithDir("/var/lib/daramjwee/objectstore"),
            objectstore.WithPrefix("prod/api-cache"),
            objectstore.WithPackThreshold(1<<20), // 1 MiB
            objectstore.WithPageSize(256<<10),            // 256 KiB
            objectstore.WithBlockCache(64<<20),     // 64 MiB
            objectstore.WithCheckpointCache(16<<20), // 16 MiB
            objectstore.WithCheckpointTTL(2*time.Second),
        ),
    ),
)

Recommended starting points:

  • WithPackThreshold(...)
    • The main cost/performance knob for current objectstore.
    • Smaller than the threshold: packed into shared remote segment objects.
    • Larger than the threshold: uploaded as direct remote blobs.
    • Start with 512 KiB ~ 1 MiB for objectstore-only tiers.
    • Start with 1 MiB ~ 2 MiB when FileStore is in front and absorbs most hot reads.
  • WithDir(...)
    • Local ingest/catalog workspace for the backend.
  • WithBlockCache(...)
    • In-process payload block cache for packed remote reads.
  • WithCheckpointCache(...)
    • In-process metadata cache for decoded shard checkpoints such as latest.json.
  • WithCheckpointTTL(...)
    • Freshness window for the checkpoint metadata cache.

See pkg/store/objectstore/README.md for a more detailed explanation of each option and suggested presets.

Documentation

Overview

Package daramjwee contains the core implementation of the Cache interface.

Index

Constants

This section is empty.

Variables

View Source
var ErrBackgroundJobRejected = errors.New("daramjwee: background job rejected")
View Source
var ErrCacheClosed = errors.New("daramjwee: cache is closed")
View Source
var ErrCacheableNotFound = errors.New("daramjwee: resource not found, but this state is cacheable")

ErrCacheableNotFound is returned when a resource is not found, but this state is cacheable (e.g., a negative cache entry).

View Source
var ErrNilFetcher = errors.New("daramjwee: nil fetcher")

ErrNilFetcher is returned when a cache operation that may call the origin is invoked without a Fetcher.

View Source
var ErrNilMetadata = errors.New("daramjwee: nil metadata encountered")
View Source
var ErrNotFound = errors.New("daramjwee: object not found")

ErrNotFound is returned when an object is not found in the cache or the origin.

View Source
var ErrNotModified = errors.New("daramjwee: resource not modified")

ErrNotModified is a sentinel error returned by a Fetcher when the resource at the origin has not changed compared to the cached version.

View Source
var ErrTopWriteInvalidated = errors.New("daramjwee: top-tier write invalidated")

Functions

func ReadAll added in v0.3.8

func ReadAll(rc io.ReadCloser) ([]byte, error)

ReadAll attempts to use safeCloser.ReadAll() if possible, otherwise falls back to io.ReadAll. This helper function allows seamless usage regardless of the underlying ReadCloser type.

Types

type BeginSetUsesContext added in v0.4.1

type BeginSetUsesContext interface {
	BeginSetUsesContext() bool
}

BeginSetUsesContext is an optional Store extension for backends whose returned sinks continue using the provided context after BeginSet returns.

type Cache

type Cache interface {
	// Get retrieves an object using the cache's current representation and
	// optional client validator request metadata.
	// On a cache miss, it uses the provided Fetcher to retrieve data from the origin.
	// The fetcher must not be nil.
	Get(ctx context.Context, key string, req GetRequest, fetcher Fetcher) (*GetResponse, error)

	// Set provides a writer to stream an object into the cache.
	// The cache entry is finalized when the returned writer is closed.
	// This pattern is ideal for use with io.MultiWriter for simultaneous
	// response-to-client and writing-to-cache scenarios.
	// NOTE: The caller is responsible for calling Close() or Abort() on the
	// returned sink to ensure resources are released.
	Set(ctx context.Context, key string, metadata *Metadata) (WriteSink, error)

	// Delete removes an object from the cache.
	Delete(ctx context.Context, key string) error

	// ScheduleRefresh asynchronously refreshes a cache entry using the provided Fetcher.
	// The fetcher must not be nil.
	ScheduleRefresh(ctx context.Context, key string, fetcher Fetcher) error

	// Close gracefully shuts down the cache and its background workers.
	Close()
}

Cache is the primary public interface for interacting with daramjwee. It enforces a memory-safe, stream-based interaction model.

func New

func New(logger log.Logger, opts ...Option) (Cache, error)

New creates and configures a new DaramjweeCache instance.

type CacheGroup added in v0.8.0

type CacheGroup interface {
	NewCache(name string, opts ...Option) (Cache, error)
	Close()
}

CacheGroup owns a shared background runtime for multiple caches.

func NewGroup added in v0.8.0

func NewGroup(logger log.Logger, opts ...GroupOption) (CacheGroup, error)

NewGroup constructs a CacheGroup with a shared bounded runtime.

type CacheRuntimeConfig added in v0.8.0

type CacheRuntimeConfig struct {
	Weight     int
	QueueLimit int
}

type Config

type Config struct {
	Tiers []Store

	WorkerStrategy string
	Workers        int
	WorkerQueue    int
	WorkerTimeout  time.Duration
	Weight         int
	QueueLimit     int

	OpTimeout    time.Duration
	CloseTimeout time.Duration

	PositiveFreshness      time.Duration
	NegativeFreshness      time.Duration
	TierFreshnessOverrides map[int]TierFreshnessOverride
	// contains filtered or unexported fields
}

Config holds all the configurable settings for the daramjwee cache.

type ConfigError

type ConfigError struct {
	Message string
}

ConfigError represents an error that occurs during the configuration process.

func (*ConfigError) Error

func (e *ConfigError) Error() string

Error returns the error message for ConfigError.

type DaramjweeCache

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

DaramjweeCache is a concrete implementation of the Cache interface.

func (*DaramjweeCache) Close

func (c *DaramjweeCache) Close()

Close safely shuts down the worker.

func (*DaramjweeCache) Delete

func (c *DaramjweeCache) Delete(ctx context.Context, key string) error

Delete sequentially deletes an object from all tiers to prevent deadlocks.

func (*DaramjweeCache) Get

func (c *DaramjweeCache) Get(ctx context.Context, key string, req GetRequest, fetcher Fetcher) (*GetResponse, error)

Get retrieves data based on the requested caching strategy. It checks ordered tiers from top to bottom and finally fetches from the origin.

func (*DaramjweeCache) ScheduleRefresh

func (c *DaramjweeCache) ScheduleRefresh(ctx context.Context, key string, fetcher Fetcher) error

ScheduleRefresh submits a background cache refresh job to the worker.

func (*DaramjweeCache) Set

func (c *DaramjweeCache) Set(ctx context.Context, key string, metadata *Metadata) (WriteSink, error)

Set returns a WriteCloser to directly write data to the cache. The data is written to tier 0.

type EvictionPolicy

type EvictionPolicy interface {
	// Touch is called when an item is accessed.
	Touch(key string)
	// Add is called when a new item is added, along with its size in bytes.
	Add(key string, size int64)
	// Remove is called when an item is explicitly deleted.
	Remove(key string)
	// Evict is called to determine which item(s) should be evicted.
	// It should return one or more keys to be removed.
	Evict() []string
}

EvictionPolicy defines the contract for a cache eviction strategy.

func NewNullEvictionPolicy

func NewNullEvictionPolicy() EvictionPolicy

NewNullEvictionPolicy creates a new no-op eviction policy.

type FetchResult

type FetchResult struct {
	Body     io.ReadCloser
	Metadata *Metadata
}

FetchResult holds the data and metadata returned from a successful fetch operation.

type FetchUsesContext added in v0.4.1

type FetchUsesContext interface {
	FetchUsesContext() bool
}

FetchUsesContext is an optional Fetcher extension for fetchers whose returned body continues using the provided context after Fetch returns.

type Fetcher

type Fetcher interface {
	Fetch(ctx context.Context, oldMetadata *Metadata) (*FetchResult, error)
}

Fetcher defines the contract for fetching an object from an origin.

type GetRequest added in v0.6.0

type GetRequest struct {
	// IfNoneMatch compares the caller's validator against the cache's current
	// representation validator.
	IfNoneMatch string
}

GetRequest controls cache read behavior.

type GetResponse added in v0.6.0

type GetResponse struct {
	Status   GetStatus
	Body     io.ReadCloser
	Metadata Metadata
}

GetResponse carries the result of a cache read decision. It also implements io.ReadCloser by delegating to Body, allowing callers to continue using io.ReadAll/Close on successful body-bearing responses.

func (*GetResponse) Close added in v0.6.0

func (r *GetResponse) Close() error

Close closes the response body when present.

func (*GetResponse) Read added in v0.6.0

func (r *GetResponse) Read(p []byte) (int, error)

Read delegates to the response body when present.

type GetStatus added in v0.6.0

type GetStatus int

GetStatus describes the result of a cache read decision.

const (
	GetStatusOK GetStatus = iota
	GetStatusNotModified
	GetStatusNotFound
)

func (GetStatus) String added in v0.6.0

func (s GetStatus) String() string

type GetStreamUsesContext added in v0.4.1

type GetStreamUsesContext interface {
	GetStreamUsesContext() bool
}

GetStreamUsesContext is an optional Store extension for backends whose returned readers continue using the provided context after GetStream returns.

type GroupConfig added in v0.8.0

type GroupConfig struct {
	Workers            int
	WorkerTimeout      time.Duration
	WorkerQueueDefault int
	CloseTimeout       time.Duration
}

GroupConfig holds the shared runtime defaults used by caches created from a CacheGroup.

type GroupOption added in v0.8.0

type GroupOption func(*GroupConfig) error

GroupOption configures a CacheGroup.

func WithGroupCloseTimeout added in v0.8.0

func WithGroupCloseTimeout(timeout time.Duration) GroupOption

WithGroupCloseTimeout sets the timeout for graceful shutdown of the shared cache group.

func WithGroupWorkerQueueDefault added in v0.8.0

func WithGroupWorkerQueueDefault(size int) GroupOption

WithGroupWorkerQueueDefault sets the default queue capacity used for group-attached caches.

func WithGroupWorkerTimeout added in v0.8.0

func WithGroupWorkerTimeout(timeout time.Duration) GroupOption

WithGroupWorkerTimeout sets the timeout applied to each shared runtime worker job.

func WithGroupWorkers added in v0.8.0

func WithGroupWorkers(count int) GroupOption

WithGroupWorkers sets the number of shared runtime workers used by caches in a group.

type JobKind added in v0.8.0

type JobKind int
const (
	JobKindRefresh JobKind = iota
	JobKindPersist
)

func (JobKind) String added in v0.8.0

func (k JobKind) String() string

type Metadata

type Metadata struct {
	CacheTag   string
	IsNegative bool
	CachedAt   time.Time
}

Metadata holds essential metadata about a cached item. It is designed to be extensible for future needs (e.g., LastModified, Size).

func (Metadata) MarshalJSON added in v0.6.0

func (m Metadata) MarshalJSON() ([]byte, error)

MarshalJSON writes the new CacheTag field name while remaining compatible with older stored metadata that used ETag on disk.

func (*Metadata) UnmarshalJSON added in v0.6.0

func (m *Metadata) UnmarshalJSON(data []byte) error

UnmarshalJSON accepts both CacheTag and the legacy ETag field names.

type Option

type Option func(cfg *Config) error

Option is a function type that modifies the Config.

func WithCloseTimeout added in v0.5.0

func WithCloseTimeout(timeout time.Duration) Option

WithCloseTimeout sets the timeout for graceful shutdown of the cache.

func WithFillLeaseTimeout added in v0.9.1

func WithFillLeaseTimeout(timeout time.Duration) Option

WithFillLeaseTimeout bounds how long a cache miss or lower-tier promotion response may hold the top-tier write lock while the caller consumes the body. The lease can preempt only after the target store's BeginSet has returned a sink; BeginSet setup time is bounded by the operation context instead. A timeout of 0 disables the lease timer; negative values are rejected.

func WithFreshness added in v0.5.0

func WithFreshness(positive, negative time.Duration) Option

WithFreshness sets the chain-wide default freshness duration for positive and negative cache entries across the ordered tier chain. A duration of 0 causes entries to be considered stale immediately.

func WithOpTimeout added in v0.5.0

func WithOpTimeout(timeout time.Duration) Option

WithOpTimeout sets the setup-stage timeout for cache operations like Get and Set.

func WithQueueLimit added in v0.8.0

func WithQueueLimit(limit int) Option

WithQueueLimit sets the bounded queue capacity for a cache attached to a shared group runtime.

func WithTierFreshness added in v0.4.1

func WithTierFreshness(index int, positive, negative time.Duration) Option

WithTierFreshness overrides the positive and negative freshness durations for a specific tier index. The configured values override the chain-wide default. A duration of 0 causes entries to be considered stale immediately.

func WithTiers added in v0.4.1

func WithTiers(stores ...Store) Option

WithTiers sets the regular cache tiers in top-to-bottom order. It replaces the entire tier chain, so any WithTierFreshness override indices are validated against the final order during cache initialization.

func WithWeight added in v0.8.0

func WithWeight(weight int) Option

WithWeight sets the relative dequeue weight for a cache attached to a shared group runtime.

func WithWorkerQueue added in v0.5.0

func WithWorkerQueue(size int) Option

WithWorkerQueue sets the queue capacity used for async background jobs.

func WithWorkerStrategy added in v0.5.0

func WithWorkerStrategy(strategy string) Option

WithWorkerStrategy sets the background worker strategy. Supported values are "pool" and "all"; unknown values return a configuration error.

func WithWorkerTimeout added in v0.5.0

func WithWorkerTimeout(timeout time.Duration) Option

WithWorkerTimeout sets the timeout applied to each background job.

func WithWorkers added in v0.5.0

func WithWorkers(count int) Option

WithWorkers sets the number of background workers used for async jobs.

type StagedWriteSink added in v0.9.1

type StagedWriteSink interface {
	io.Writer
	Commit(ctx context.Context) error
	Abort() error
}

StagedWriteSink is an optional store capability used by the cache core to separate invisible staging from visible publish.

type StagingStore added in v0.9.1

type StagingStore interface {
	BeginStagedSet(ctx context.Context, key string, metadata *Metadata) (StagedWriteSink, error)
}

StagingStore is an optional Store extension. Stores that implement it let the cache coordinate only the short commit phase instead of holding cache-level same-key ownership for the full caller-controlled write lifetime. BeginStagedSet must keep the currently readable value for key unchanged until Commit succeeds. Abort and failed Commit must leave no visible value from the staged writer, and Delete must not wait for an uncommitted staged writer on the same key to commit or abort. Commit and Abort are terminal and must tolerate repeated cleanup calls because the cache may call Abort after a failed Commit to release hidden staging resources.

type Store

type Store interface {
	// GetStream retrieves an object and its metadata as a stream.
	// Successful lookups must return non-nil metadata.
	GetStream(ctx context.Context, key string) (io.ReadCloser, *Metadata, error)
	// BeginSet returns a sink that stages data into the store.
	// The currently readable value for key must remain unchanged until the
	// returned sink is successfully closed or aborted.
	// Implementations should honor ctx during setup; cache-level fill leases
	// start only after BeginSet returns and cannot interrupt a blocked BeginSet.
	BeginSet(ctx context.Context, key string, metadata *Metadata) (WriteSink, error)
	// Delete removes the last committed object from the store.
	// It must not wait for an uncommitted BeginSet sink on the same key to
	// close or abort.
	Delete(ctx context.Context, key string) error
	// Stat retrieves metadata for an object without its data.
	Stat(ctx context.Context, key string) (*Metadata, error)
}

Store defines the interface for a single cache storage tier (e.g., memory, disk).

type TierFreshnessOverride added in v0.5.0

type TierFreshnessOverride struct {
	Positive time.Duration
	Negative time.Duration
}

type TierValidator added in v0.4.1

type TierValidator interface {
	ValidateTier(index int) error
}

TierValidator is an optional Store extension for stores that restrict which positions they can safely occupy in the ordered tier chain.

type WriteSink added in v0.4.1

type WriteSink interface {
	io.WriteCloser
	Abort() error
}

WriteSink is the terminal write contract for cache stores. Close publishes the staged write, and Abort discards it. Implementations used by Store.BeginSet must not call back into Cache methods from Close or Abort because cache tiers may coordinate commit ordering while those terminal methods run. Write must return promptly when the underlying storage rejects or stalls a staged write; cache-level fill preemption cannot interrupt a blocked Write.

Jump to

Keyboard shortcuts

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