daramjwee

package module
v0.15.2 Latest Latest
Warning

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

Go to latest
Published: Oct 8, 2026 License: Apache-2.0 Imports: 15 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. Stop all older writers before upgrading a shared prefix; use a fresh prefix for an overlapping rollout. 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".

Promotion Probation

Lower-tier hits promote into the top tier by default. When the workload has many one-hit wonders (keys accessed exactly once), those promotions pollute the hot tier without benefit. Enable 2-hit probation to promote a key only on its second lower-tier hit:

cache, err := daramjwee.New(
    logger,
    daramjwee.WithTiers(memTier, fileTier),
    daramjwee.WithPromotionProbation(65536), // bounded FIFO set
)

maxEntries bounds the probation set with FIFO eviction. Deleting a key resets its probation state. The cost is one extra lower-tier read per cold key.

Concurrent misses for the same key are coalesced: the first caller fetches from the origin while filling the top tier, and concurrent waiters are served from the top tier once the fill publishes (bounded by their request deadline).

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

View Source
const (
	DropReasonRejected = runtime.DropReasonRejected
	DropReasonShutdown = runtime.DropReasonShutdown
)
View Source
const (
	JobKindRefresh = runtime.JobKindRefresh
	JobKindPersist = runtime.JobKindPersist
)

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 ErrCommitOutcomeUnknown = errors.New("daramjwee: commit outcome is unknown")

ErrCommitOutcomeUnknown means a staged commit reached durable storage but its final outcome could not be confirmed. The store must fail closed until it is reconciled or reopened safely.

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 ErrReadStateUncertain = errors.New("daramjwee: read state is uncertain")

ErrReadStateUncertain means a higher tier owns a newer generation but cannot currently prove its contents. Callers must not fall through to older tiers.

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 AdmissionPolicy added in v0.15.0

type AdmissionPolicy int

AdmissionPolicy controls lower-tier promotion.

const (
	AdmissionAllowed AdmissionPolicy = iota
	AdmissionDeferred
)

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 BodySpec added in v0.15.0

type BodySpec int
const (
	BodyNone BodySpec = iota
	BodyDirect
	BodyStream
)

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 = runtime.Config

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

	PromotionProbationEntries int
	// 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. Multiple calls are safe: the first call performs the shutdown and subsequent calls block until the first shutdown completes, then return the same result.

func (*DaramjweeCache) Delete

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

Delete sequentially deletes an object from all tiers to prevent deadlocks. Deletion order is bottom-up: lower tiers are deleted before the top tier to prevent resurrection of deleted values through lower-tier promotion.

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 DropReason added in v0.15.0

type DropReason = runtime.DropReason

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 FanoutSpec added in v0.15.0

type FanoutSpec int
const (
	FanoutNone FanoutSpec = iota
	FanoutAfterPublish
)

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 Freshness added in v0.15.0

type Freshness int

Freshness classifies a cache entry by its freshness window.

const (
	FreshnessFresh Freshness = iota
	FreshnessStale
)

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 Job added in v0.15.0

type Job = runtime.Job

type JobKind added in v0.8.0

type JobKind = runtime.JobKind

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 CloneMetadata added in v0.9.2

func CloneMetadata(meta *Metadata) *Metadata

CloneMetadata returns a deep copy of the given Metadata. It returns nil if meta is nil.

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 Observation added in v0.15.0

type Observation struct {
	Source             SourceKind
	SourceTier         int
	EntryNegative      bool
	Freshness          Freshness
	ConditionalMatched bool
	UpperTiersHealth   UpperTierHealth
	Admission          AdmissionPolicy
	HasTopStore        bool
}

Observation contains the I/O-free facts used to plan a cache read.

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 WithPromotionProbation added in v0.14.1

func WithPromotionProbation(maxEntries int) Option

WithPromotionProbation enables 2-hit probation for lower-tier promotion: the first lower-tier hit for a key is served without promoting to the top tier, and only the second hit promotes. maxEntries bounds the probation set with FIFO eviction. This prevents one-hit wonders from polluting the hot tier at the cost of one extra lower-tier read per cold key.

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 Outcome added in v0.15.0

type Outcome int

Outcome describes how a read session ended.

const (
	OutcomeEOF Outcome = iota
	OutcomeEarlyClose
	OutcomeReadError
	OutcomeCancelled
	OutcomePreempted
)

type Planner added in v0.15.0

type Planner struct{}

Planner converts observations into read plans without performing I/O.

func (*Planner) Plan added in v0.15.0

func (p *Planner) Plan(obs Observation) ReadPlan

type PublishSpec added in v0.15.0

type PublishSpec int
const (
	PublishNone PublishSpec = iota
	PublishOnEOF
)

type ReadPlan added in v0.15.0

type ReadPlan struct {
	Reply   ReplySpec
	Body    BodySpec
	Publish PublishSpec
	Refresh RefreshSpec
	Fanout  FanoutSpec
}

ReadPlan describes the response and side effects for a cache read.

type ReadSession added in v0.15.0

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

ReadSession releases resources owned by one Get call exactly once.

func (*ReadSession) Finish added in v0.15.0

func (s *ReadSession) Finish(outcome Outcome) error

Finish releases all resources. Repeated calls are harmless.

func (*ReadSession) SetMissLeader added in v0.15.0

func (s *ReadSession) SetMissLeader(_ *missLead, release func())

SetMissLeader arranges for miss leadership to be released with the session.

func (*ReadSession) WaitForPublish added in v0.15.0

func (s *ReadSession) WaitForPublish()

WaitForPublish blocks until Finish is called.

type RefreshSpec added in v0.15.0

type RefreshSpec int
const (
	RefreshNone RefreshSpec = iota
	RefreshOnClose
)

type ReplySpec added in v0.15.0

type ReplySpec int
const (
	ReplyOK ReplySpec = iota
	ReplyNotModified
	ReplyNotFound
)

type SourceKind added in v0.15.0

type SourceKind int

SourceKind identifies where a cache lookup found an entry.

const (
	SourceNone SourceKind = iota
	SourceTop
	SourceLower
)

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 confirmed failed Commit must leave no visible value from the staged writer. An error matching ErrCommitOutcomeUnknown is an indeterminate outcome, not a confirmed failure; the store must fail closed until reconciliation. 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 UpperTierHealth added in v0.15.0

type UpperTierHealth int

UpperTierHealth reports whether every tier above the source was readable.

const (
	UpperTiersClean UpperTierHealth = iota
	UpperTiersDirty
)

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.

Directories

Path Synopsis
cmd
daramjwee command
cache_group command
file_objstore command
filestore_policy command
Package main demonstrates basic FileStore usage with LRU eviction policy.
Package main demonstrates basic FileStore usage with LRU eviction policy.
generic_cache command
memstore_lru command
memstore_s3fifo command
memstore_sieve command
internal
stripedlock
Package stripedlock provides a striped lock manager for reducing contention by distributing keys across a fixed number of mutexes.
Package stripedlock provides a striped lock manager for reducing contention by distributing keys across a fixed number of mutexes.
pkg

Jump to

Keyboard shortcuts

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