Help us shape the future of Go! Take the 2026 Go Developer Survey

storage

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Nov 1, 2026 License: MIT Imports: 9 Imported by: 0

Documentation

Overview

Package storage owns atomic cache values, counters, expiry and sweeping.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Store

type Store struct {
	// Mu guards entries.
	Mu sync.Mutex

	// Entries maps keys to stored values, including expired ones the janitor
	// or a later access has not removed yet.
	Entries map[string]entry

	// Cleanup is the interval between sweeps, inline and janitor. Negative
	// disables sweeping.
	Cleanup time.Duration

	// LastSweep is when expired entries were last removed in bulk. Guarded
	// by mu.
	LastSweep time.Time

	// Lifecycle guards cancel and done.
	Lifecycle sync.Mutex

	// Cancel ends the janitor goroutine. It is nil until Start runs.
	Cancel context.CancelFunc

	// Done closes when the janitor goroutine exits.
	Done chan struct{}
}

Store implements cache.Driver in process memory.

Store is suitable for single-process applications, local development, and tests where persistence across restarts is not required. It is safe for concurrent use. Expiry is checked on access, so correctness never depends on sweeping. Writes sweep expired entries inline at most once per [Builder.Cleanup], so memory stays bounded without calling Store.Start.

Example:

driver := memorycache.New()
if err := driver.Put(ctx, "version", []byte("v1"), time.Minute); err != nil {
	return err
}

func NewStore

func NewStore(cleanup time.Duration) *Store

NewStore constructs the atomic store without starting background work.

func (*Store) Add

func (driver *Store) Add(ctx context.Context, key string, value []byte, ttl time.Duration) (bool, error)

Add stores a copy of value only when key is absent or expired.

Example:

added, err := driver.Add(ctx, "receipt", []byte("claimed"), time.Hour)
if err != nil {
	return err
}
_ = added

func (*Store) CompareAndPut

func (driver *Store) CompareAndPut(ctx context.Context, match cache.Match, entry cache.Entry) (bool, error)

CompareAndPut compares the existing guard and writes entry under one mutex. Missing, expired, or unequal guards return false without changing logical values or lifetimes. A matching guard permits a copied replacement with the requested TTL; a different guard key retains its content and expiry. Cancellation is checked both before waiting and after acquiring the mutex.

Example:

driver := memorycache.New()
if err := driver.Put(ctx, "document", []byte("draft"), time.Minute); err != nil {
	return err
}
applied, err := driver.CompareAndPut(ctx,
	cache.Match{Key: "document", Value: []byte("draft")},
	cache.Entry{Key: "document", Value: []byte("revised"), TTL: time.Minute})
if err != nil {
	return err
}
_ = applied

func (*Store) Decrement

func (driver *Store) Decrement(ctx context.Context, key string, delta int64) (int64, error)

Decrement decreases the integer stored at key by delta and returns the new value. A missing key is created from zero. The remaining TTL is preserved. Subtraction is checked directly, including delta == math.MinInt64. A result outside int64 returns an error wrapping strconv.ErrRange without changing the target value or expiry; a missing target remains absent.

Example:

count, err := driver.Decrement(ctx, "available", 1)
if err != nil {
	return err
}
_ = count

func (*Store) Delete

func (driver *Store) Delete(ctx context.Context, key string) error

Delete removes key. Deleting a missing key is a no-op.

Example:

if err := driver.Delete(ctx, "version"); err != nil {
	return err
}

func (*Store) ExtendLock

func (driver *Store) ExtendLock(ctx context.Context, name, token string, ttl time.Duration) (bool, error)

ExtendLock resets the expiry of name to ttl from now only while it holds token and reports whether it did. It returns an error wrapping cache.ErrInvalidTTL when ttl is not positive.

Example:

extended, err := driver.ExtendLock(ctx, "lock:report", token, time.Minute)
if err != nil {
	return err
}
_ = extended

func (*Store) Get

func (driver *Store) Get(ctx context.Context, key string) ([]byte, error)

Get returns a copy of the bytes stored for key. It returns cache.ErrNotFound when the key does not exist or has expired.

Example:

raw, err := driver.Get(ctx, "version")
if err != nil {
	return err
}
_ = raw // Independent bytes; mutation does not alter the stored value.

func (*Store) Has

func (driver *Store) Has(ctx context.Context, key string) (bool, error)

Has reports whether key exists and has not expired.

Example:

found, err := driver.Has(ctx, "version")
if err != nil {
	return err
}
_ = found // Observation only; use an atomic operation to guard a write.

func (*Store) Increment

func (driver *Store) Increment(ctx context.Context, key string, delta int64) (int64, error)

Increment increases the integer stored at key by delta and returns the new value. A missing key is created from zero. The remaining TTL is preserved. A mathematical result outside int64 returns an error wrapping strconv.ErrRange without changing the target value or TTL. An absent target remains absent.

Example:

count, err := driver.Increment(ctx, "views", 1)
if err != nil {
	return err
}
_ = count

func (*Store) IncrementWithTTL

func (driver *Store) IncrementWithTTL(ctx context.Context, key string, delta int64, ttl time.Duration) (int64, time.Duration, error)

IncrementWithTTL increases the integer stored at key by delta, ensures the key expires, and returns the new value with the remaining lifetime. A key that is missing or has no expiry gets ttl; an existing expiry is kept. It returns an error when ttl is not positive. A mathematical result outside int64 returns an error wrapping strconv.ErrRange before changing the target value or expiry. Overflow never adds a TTL to a persistent target or creates an absent one.

Example:

count, remaining, err := driver.IncrementWithTTL(ctx, "requests", 1, time.Minute)
if err != nil {
	return err
}
_, _ = count, remaining

func (*Store) Pull

func (driver *Store) Pull(ctx context.Context, key string) ([]byte, error)

Pull returns the bytes stored for key and removes the entry under the same lock, so concurrent callers cannot both receive the value. It returns cache.ErrNotFound when the key does not exist or has expired.

Example:

raw, err := driver.Pull(ctx, "one-time-token")
if err != nil {
	return err
}
_ = raw // The entry was removed atomically with this read.

func (*Store) Put

func (driver *Store) Put(ctx context.Context, key string, value []byte, ttl time.Duration) error

Put stores a copy of value for key. A zero ttl stores the entry without expiration.

Example:

if err := driver.Put(ctx, "version", []byte("v1"), time.Minute); err != nil {
	return err
}

func (*Store) Start

func (driver *Store) Start(ctx context.Context) error

Start launches the janitor unless Cleanup is negative. Starting an already started driver does nothing.

Example:

driver := memorycache.New()
if err := driver.Start(ctx); err != nil {
	return err
}
return driver.Stop(ctx)

func (*Store) Stop

func (driver *Store) Stop(ctx context.Context) error

Stop ends the janitor and waits for it to exit or ctx to end. The driver stays usable afterwards; expired entries are then removed by access and by inline sweeps on writes.

Example:

cleanup, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
if err := driver.Stop(cleanup); err != nil {
	return err
}

func (*Store) TTL

func (driver *Store) TTL(ctx context.Context, key string) (time.Duration, error)

TTL returns the remaining lifetime for key, or zero when it never expires. It returns cache.ErrNotFound when the key does not exist or has expired.

Example:

remaining, err := driver.TTL(ctx, "version")
if err != nil {
	return err
}
_ = remaining

func (*Store) TryLock

func (driver *Store) TryLock(ctx context.Context, name, token string, ttl time.Duration) (bool, error)

TryLock stores token under name for ttl when name is absent or expired, and reports whether it did. The entry is an ordinary one, so an expired lock is replaced like any expired key. It returns an error wrapping cache.ErrInvalidTTL when ttl is not positive.

Example:

token := rand.Text() // crypto/rand: unique to this attempt.
held, err := driver.TryLock(ctx, "lock:report", token, time.Minute)
if err != nil {
	return err
}
if held {
	_, err = driver.Unlock(ctx, "lock:report", token)
}
return err

func (*Store) Unlock

func (driver *Store) Unlock(ctx context.Context, name, token string) (bool, error)

Unlock removes name only while it holds token and reports whether it did, comparing and deleting under one lock acquisition.

Example:

released, err := driver.Unlock(ctx, "lock:report", token)
if err != nil {
	return err
}
_ = released

Jump to

Keyboard shortcuts

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