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

storage

package
v1.3.0 Latest Latest
Warning

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

Go to latest
Published: Oct 6, 2026 License: AGPL-3.0 Imports: 32 Imported by: 0

README

Storage Package

This package provides the storage abstraction layer for Starport, supporting multiple backend implementations with a unified interface.

Overview

The storage package defines a KVStore interface that abstracts key-value storage operations. It supports:

  • Basic CRUD operations (Get, Set, Delete, Exists)
  • TTL (Time-To-Live) support for temporary data
  • Atomic operations (Increment, Decrement, CompareAndSwap)
  • Batch operations for efficiency
  • Atomic conditional mutation sets through CompareAndSwapBatch
  • Scanning/listing capabilities

Architecture

storage/
├── interface.go      # KVStore interface and configuration types
├── open.go          # Open function for creating storage instances
├── serialization.go  # Helpers for data serialization
├── mock.go          # In-memory mock implementation for testing
└── README.md        # This file

Storage Backends

Badger (Default)
  • Embedded key-value store
  • Zero external dependencies
  • Excellent performance (<1ms latency)
  • Perfect for single-node deployments
Valkey
  • Redis-compatible distributed store
  • Required for multi-node deployments
  • Supports shared state across instances
  • Higher latency (<5ms) but better scalability

Usage

// Create storage instance
config := storage.Config{
    Type: "badger",
    Badger: storage.BadgerConfig{
        Path: "./data/badger",
    },
}

store, err := storage.Open(config)
if err != nil {
    return err
}
defer store.Close()

// Basic operations
ctx := context.Background()
err = store.Set(ctx, "key", []byte("value"))
value, err := store.Get(ctx, "key")

// TTL operations
err = store.SetWithTTL(ctx, "temp-key", []byte("temp-value"), 5*time.Minute)

// Atomic operations
count, err := store.Increment(ctx, "counter", 1)

// Apply all conditional mutations or none. A nil expectation requires absence.
err = store.CompareAndSwapBatch(ctx, []storage.CompareAndSwapMutation{
    {Key: "key1", ExpectedValue: nil, NewValue: []byte("value1")},
    {Key: "key2", ExpectedValue: nil, NewValue: []byte("value2")},
})

Testing

MockStore implements the full KVStore interface in memory. Use it for unit tests without external dependencies.

// Create mock store for testing
store := storage.NewMockStore()
defer store.Close()

// Use exactly like a real store
err := store.Set(ctx, "test-key", []byte("test-value"))

Key Patterns

The storage layer uses consistent key patterns for different data types:

  • API Keys: apikey:{hash}
  • Presets: presets:v1:name:{base64url(name)}
  • BYOK Credentials: credential:{api_key_id}:{provider}
  • Filters: filter:{name}
  • Rate Limits: ratelimit:{key}:{window}

Performance Considerations

  1. Batch Operations: Use batch operations when working with multiple keys to reduce round trips
  2. TTL Usage: Use TTL for temporary data to avoid manual cleanup
  3. Conditional writes: Use CompareAndSwapBatch when writes must succeed together. A conflict changes no keys.
  4. Key Design: Use hierarchical key patterns for efficient scanning

Error Handling

The package defines specific error types:

  • ErrNotFound: Key does not exist
  • ErrConflict: Write conflict (e.g., CAS mismatch)
  • ErrInvalidKey: Invalid key format
  • ErrStorageClosed: An operation used a closed storage instance
  • ErrTimeout: Operation timed out

Always check for ErrNotFound when a key might not exist:

value, err := store.Get(ctx, "maybe-missing")
if errors.Is(err, storage.ErrNotFound) {
    // Handle missing key
}

Deployment identity

Production composition opens storage through Config.RuntimeStorage(). That projection supplies the canonical STARPORT_DEPLOYMENT_ID from the resolved product paths. OpenValkey refuses an absent or invalid identity before it opens a connection.

Durable keys and notification channels use {starport:v1:<base64url-deployment-id>:}kv: before the logical repository key or channel. The identity uses unpadded UTF-8 encoding. One deployment occupies one hash slot, including multi-key conditional mutations. This layout does not qualify Cluster support. Cache-only service keys retain their separate cache prefix and service configuration.

The qualified release is Valkey 7.2.14 as one standalone writable primary. QualifiedValkeyVersion names it, CI pins the same release by image digest, and TestQualifiedValkeyVersion compares a real service against it. Another release or Cluster mode is UNVERIFIED until a qualification run records it.

The incarnation identity is run_id:master_replid. A restart, a promotion, and the first replica attachment to a primary without a replication backlog each change it. A bound owner then fails closed until re-admission. Attach replicas before admission, or plan a re-admission after the first attachment. TestValkeyDurabilityAndFailover records this limit.

Repositories use logical keys. The adapter adds the physical prefix and removes it from scan results and notification callbacks. Bounded reads, native ownership checks, and fresh-initialization claims use the same physical prefix. Fresh initialization still requires an empty dedicated KV database and SQL schema. A namespace alone cannot approve populated state or a replacement backend.

Existing unprefixed records require an explicit migration before use. Ordinary startup never copies them or falls back to them. Badger retains its local layout inside private product storage.

Portable recovery records

OpenRecordTransfer exposes stopped-deployment export and conditional import for Badger and Valkey. The caller must stop and fence source and target writers. Valkey operations bind every scan, read, and write to the selected backend process identity. This contract does not qualify automatic failover.

Each record retains its exact key, value, and absolute expiry. Values have a 64-MiB transfer limit. Keys have a 65,000-byte limit.

Badger rounds millisecond expiry down to whole seconds. Import reports each adjustment and never extends the original deadline. An expired record cannot become persistent. Persistent records retain zero expiry.

The recovery package writes a private SQLite image and its digest receipt. The image needs no WAL sidecar. It deduplicates identical scan records and rejects changed duplicates. Import verifies a private copy and all records before it claims an empty target namespace. An exact retry accepts only the same operation and image.

Import retains storage:import:v1 after success or failure. Ordinary Open refuses that barrier before application startup. The recovery coordinator must retain it until every deployment component and the independent recovery evidence pass verification. Raw backend constructors permit explicit recovery access. They do not approve admission.

Badger import requires persistent storage with sync_writes enabled. Valkey namespace inspection requires externally fenced writers. Its final claim and each record write use native conditional operations. Other deployment namespaces remain unchanged. Fresh fleet initialization retains its separate empty-database requirement.

These components do not complete deployment recovery. The full procedure still requires matching SQL, blobs, catalog state, credential access, and post-backup revocation and spending evidence.

Reconcile imported records in order

ImportReconciler applies domain-validated changes while the exact import barrier remains present. Domain owners supply persistent-record comparisons and replacements. Operator input must use those owners, not arbitrary storage keys. Each operation binds its claim, sequence, previous receipt digest, independent evidence digest, and complete mutation digest. The native transaction compares every preimage before it writes records, the receipt, and the current sequence.

Badger requires persistent storage with sync_writes enabled. Valkey checks its bound process and replication identity in the same script as the writes. The operation supports at most 128 mutations and 4 MiB of key and value bytes per step. It refuses expiring records, sequence gaps, changed evidence, changed mutations, and storage-control keys. An exact completed retry returns its receipt without restoring earlier values. Concurrent conflicts require an exact retry or operator investigation.

The first successful reconciliation prevents further snapshot import, including an exact preparation retry. Resume from the ordered reconciliation receipts. Do not restart bundle preparation after this boundary. Portable backups preserve historical receipts and omit the current native sequence. Historical receipts alone cannot authorize reconciliation after another restore.

These transactions do not prove external evidence completeness, reconstruct domain history, or permit admission. The recovery coordinator must validate permission, accounting, execution, and cross-store references before it releases any barrier.

Documentation

Overview

Package storage provides a key-value storage abstraction layer with support for multiple backend implementations including embedded and distributed stores.

Package storage provides a key-value storage abstraction layer with support for multiple backend implementations including embedded and distributed stores.

Index

Constants

View Source
const (
	// StorageTypeBadger represents the Badger embedded storage backend
	StorageTypeBadger = "badger"
	// StorageTypeValkey represents the Valkey distributed storage backend
	StorageTypeValkey = "valkey"
)

Storage backend type constants

View Source
const (
	// TransferBarrierKey blocks ordinary startup until deployment recovery completes.
	TransferBarrierKey = "storage:import:v1"
	// TransferMaxKeyBytes is the portable Badger key limit.
	TransferMaxKeyBytes = 65000
	// TransferMaxValueBytes bounds one transfer allocation.
	TransferMaxValueBytes = 64 << 20
)
View Source
const (

	// ImportReplayMaxBytes bounds total keys, preimages, and new values in one native replay step.
	ImportReplayMaxBytes = 4 << 20
)
View Source
const KeyPrefixExtraction = "extraction:"

KeyPrefixExtraction is the prefix for cached document text. A parser engine writes here and the response cache writes above, and the two never share a key: one holds what a model answered, and this one holds what a document says.

View Source
const KeyPrefixResponse = "response:"

KeyPrefixResponse is the prefix for cached responses. The response cache manager composes record keys from it; Starmap owns model and provider facts, so no model metadata is keyed here.

View Source
const QualifiedValkeyVersion = "7.2.14"

QualifiedValkeyVersion is the Valkey release that the real-backend qualification covers. CI pins the same release by image digest, and TestQualifiedValkeyVersion compares a real service against it. The qualified mode is one standalone writable primary. Another release or Cluster mode is UNVERIFIED until a qualification run records it here.

Variables

View Source
var (
	// ErrNotFound is returned when a key does not exist
	ErrNotFound = errors.New("key not found")
	// ErrValueTooLarge refuses a value before copying or returning its payload.
	ErrValueTooLarge = errors.New("stored value exceeds read limit")
	// ErrInvalidReadLimit reports a non-positive bounded read limit.
	ErrInvalidReadLimit = errors.New("invalid storage read limit")
	// ErrConflict is returned when a write conflict occurs (e.g., CAS mismatch)
	ErrConflict = errors.New("write conflict")
	// ErrInvalidKey is returned when an invalid key is provided
	ErrInvalidKey = errors.New("invalid key")
	// ErrInvalidMutation is returned when an atomic mutation set is malformed.
	ErrInvalidMutation = errors.New("invalid compare-and-swap mutation")
	// ErrStorageClosed is returned when operations are attempted on a closed store
	ErrStorageClosed = errors.New("storage closed")
	// ErrReadOnly is returned when a write is attempted through a read-only store.
	ErrReadOnly = errors.New("storage is read-only")
	// ErrReadOnlyRecoveryRequired means that a non-mutating inspection cannot
	// open storage until the normal writable startup performs recovery.
	ErrReadOnlyRecoveryRequired = errors.New("storage recovery is required before read-only inspection")
	// ErrReadOnlyUnsupported means that the storage backend cannot provide a
	// non-mutating inspection on the current platform.
	ErrReadOnlyUnsupported = errors.New("read-only storage inspection is unsupported")
	// ErrTimeout is returned when an operation times out
	ErrTimeout = errors.New("operation timeout")
)

Common errors returned by storage operations

View Source
var ErrDatabaseNotEmpty = errors.New("KV database is not empty or has a different initialization claim")

ErrDatabaseNotEmpty refuses fresh initialization over an existing KV database.

View Source
var ErrImportRestricted = errors.New("storage import requires deployment recovery before startup")

ErrImportRestricted refuses startup until the deployment recovery procedure completes.

View Source
var ErrIncarnationChanged = errors.New("storage backend incarnation changed")

ErrIncarnationChanged refuses an operation on an unapproved backend process or role.

View Source
var ErrInvalidScan = errors.New("invalid storage scan")

ErrInvalidScan reports an invalid scan cursor or work hint.

View Source
var ErrPopulatedImportUnsupported = errors.New("populated import claim requires Valkey storage")

ErrPopulatedImportUnsupported refuses a populated claim on a backend without an atomic native claim.

View Source
var (
	// PubSub-related errors
	// ErrPubSubClosed is returned when operations are attempted on a closed PubSub client
	ErrPubSubClosed = errors.New("pubsub client is closed")
)

PubSub errors

View Source
var ErrTimeWindowChanged = errors.New("storage authority time left the selected window")

ErrTimeWindowChanged refuses a write outside its authority-time interval.

Functions

func CheckImportBarrier added in v1.3.0

func CheckImportBarrier(ctx context.Context, store KVStore) error

CheckImportBarrier refuses a partial or unapproved restored deployment.

func Deserialize

func Deserialize(data []byte, v any) error

Deserialize converts bytes to the specified type using JSON decoding

func DeserializeInt64

func DeserializeInt64(data []byte) (int64, error)

DeserializeInt64 converts bytes to int64

func DeserializeString

func DeserializeString(data []byte) string

DeserializeString converts bytes to string

func ImportExpiringReconciliationSHA256 added in v1.3.0

func ImportExpiringReconciliationSHA256(claim []byte, sequence int64, previous, evidence string, records []TransferRecord) (string, error)

ImportExpiringReconciliationSHA256 derives the exact deletion receipt without native effects. Original absolute expiry remains in the fingerprint even when native expiry uses lower precision.

func ImportReconciliationSHA256 added in v1.3.0

func ImportReconciliationSHA256(claim []byte, sequence int64, previous, evidence string, mutations []CompareAndSwapMutation) (string, error)

ImportReconciliationSHA256 derives the owner's exact receipt identity without native effects. It applies the same claim, mutation, presence, count, and byte checks as native replay.

func OpenImportTarget added in v1.3.0

func OpenImportTarget(ctx context.Context, config Config) (KVStore, RecordTransfer, error)

OpenImportTarget opens native recovery access without clearing import barriers. It does not start application maintenance or approve an existing backend incarnation. The caller must fence all target writers before opening it.

func Serialize

func Serialize(v any) ([]byte, error)

Serialize converts any value to bytes using JSON encoding

func SerializeInt64

func SerializeInt64(n int64) []byte

SerializeInt64 converts an int64 to bytes

func SerializeString

func SerializeString(s string) []byte

SerializeString converts a string to bytes

Types

type BadgerBackup added in v1.3.0

type BadgerBackup struct {
	Format string `json:"format"`
	Size   int64  `json:"size"`
	SHA256 string `json:"sha256"`
}

BadgerBackup binds one complete export to its exact bytes. The deployment manifest must retain this record independently of the payload.

type BadgerConfig

type BadgerConfig struct {
	GCInterval     time.Duration
	GCDiscardRatio float64
	Path           string `env:"PATH,default=./data/badger"`
	InMemory       bool
	SyncWrites     bool   `env:"SYNC_WRITES,default=true"`
	Compression    string `env:"COMPRESSION,default=snappy"`
	NumVersions    int    `env:"NUM_VERSIONS,default=1"`
	NumLevelZero   int    `env:"NUM_LEVEL_ZERO,default=5"`
	MemTableSize   int64  `env:"MEM_TABLE_SIZE,default=67108864"` // 64MB
}

BadgerConfig represents Badger-specific configuration

type BadgerRestoreResult added in v1.3.0

type BadgerRestoreResult struct {
	Published bool
}

BadgerRestoreResult reports whether the candidate reached its destination. A durability error after publication can return Published with an error.

func RestoreBadger added in v1.3.0

func RestoreBadger(ctx context.Context, config BadgerConfig, source string, expected BadgerBackup) (result BadgerRestoreResult, resultErr error)

RestoreBadger validates a backup and publishes a new persistent database. Existing destinations remain untouched. The caller must not admit traffic before deployment recovery completes.

type BadgerStore

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

BadgerStore implements the KVStore interface using Badger DB

func OpenBadger

func OpenBadger(config BadgerConfig) (*BadgerStore, error)

OpenBadger creates a new BadgerStore instance with the given configuration

func OpenBadgerReadOnly added in v1.0.1

func OpenBadgerReadOnly(config BadgerConfig) (*BadgerStore, error)

OpenBadgerReadOnly opens an existing Badger database without background maintenance or filesystem creation.

func (*BadgerStore) AuthorityTime added in v1.3.0

func (s *BadgerStore) AuthorityTime(ctx context.Context) (time.Time, error)

AuthorityTime returns host time for the standalone Badger authority.

func (*BadgerStore) Backup

func (s *BadgerStore) Backup(ctx context.Context, path string) (receipt BadgerBackup, resultErr error)

Backup exports committed records without replacing an existing backup file. This snapshot covers only Badger. The deployment coordinator owns cross-store consistency.

func (*BadgerStore) BatchDelete

func (s *BadgerStore) BatchDelete(_ context.Context, keys []string) error

BatchDelete removes multiple keys

func (*BadgerStore) BatchGet

func (s *BadgerStore) BatchGet(_ context.Context, keys []string) (map[string][]byte, error)

BatchGet retrieves multiple values by keys

func (*BadgerStore) BatchSet

func (s *BadgerStore) BatchSet(_ context.Context, items map[string][]byte) error

BatchSet stores multiple key-value pairs

func (*BadgerStore) BatchSetWithTTL

func (s *BadgerStore) BatchSetWithTTL(_ context.Context, items map[string][]byte, ttl time.Duration) error

BatchSetWithTTL stores multiple key-value pairs with TTL

func (*BadgerStore) Close

func (s *BadgerStore) Close() error

Close closes the store

func (*BadgerStore) CompareAndSwap

func (s *BadgerStore) CompareAndSwap(ctx context.Context, key string, old, newValue []byte) error

CompareAndSwap atomically updates a value if it matches the expected value

func (*BadgerStore) CompareAndSwapBatch

func (s *BadgerStore) CompareAndSwapBatch(ctx context.Context, mutations []CompareAndSwapMutation) error

CompareAndSwapBatch applies all conditional writes or none of them.

func (*BadgerStore) CompareAndSwapInWindow added in v1.3.0

func (s *BadgerStore) CompareAndSwapInWindow(ctx context.Context, mutations []CompareAndSwapMutation, window TimeWindow) error

CompareAndSwapInWindow checks the host clock in the Badger transaction.

func (*BadgerStore) Decrement

func (s *BadgerStore) Decrement(ctx context.Context, key string, delta int64) (int64, error)

Decrement atomically decrements a counter

func (*BadgerStore) Delete

func (s *BadgerStore) Delete(_ context.Context, key string) error

Delete removes a key

func (*BadgerStore) Exists

func (s *BadgerStore) Exists(_ context.Context, key string) (bool, error)

Exists checks if a key exists

func (*BadgerStore) ExpireAt

func (s *BadgerStore) ExpireAt(ctx context.Context, key string, expireAt time.Time) error

ExpireAt sets a key to expire at a specific time

func (*BadgerStore) Get

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

Get retrieves a value by key

func (*BadgerStore) GetBounded added in v1.3.0

func (s *BadgerStore) GetBounded(ctx context.Context, key string, maxBytes int) ([]byte, error)

GetBounded checks the stored size before copying the value.

func (*BadgerStore) GetTTL

func (s *BadgerStore) GetTTL(_ context.Context, key string) (time.Duration, error)

GetTTL returns the TTL for a key

func (*BadgerStore) Increment

func (s *BadgerStore) Increment(_ context.Context, key string, delta int64) (int64, error)

Increment atomically increments a counter

func (*BadgerStore) Ping

func (s *BadgerStore) Ping(ctx context.Context) error

Ping checks if the store is healthy

func (*BadgerStore) ReadBatchWithLifetime added in v1.3.0

func (s *BadgerStore) ReadBatchWithLifetime(ctx context.Context, keys []string, maxBytes int) ([]LifetimeValue, error)

ReadBatchWithLifetime reads bounded values from one Badger snapshot. A failure returns no partial batch. Missing records retain their result slots.

func (*BadgerStore) ReadWithLifetime added in v1.3.0

func (s *BadgerStore) ReadWithLifetime(ctx context.Context, key string, maxBytes int) ([]byte, time.Duration, error)

ReadWithLifetime reads payload and expiry from the same Badger snapshot.

func (*BadgerStore) Scan

func (s *BadgerStore) Scan(_ context.Context, pattern string, limit int) ([]string, error)

Scan returns keys matching a pattern

func (*BadgerStore) ScanPage added in v1.3.0

func (s *BadgerStore) ScanPage(ctx context.Context, prefix, cursor string, count int) (KeyPage, error)

ScanPage reads one page after the prior key in a Badger snapshot.

func (*BadgerStore) ScanWithPrefix

func (s *BadgerStore) ScanWithPrefix(_ context.Context, prefix string, limit int) ([]string, error)

ScanWithPrefix returns keys with a specific prefix

func (*BadgerStore) Set

func (s *BadgerStore) Set(_ context.Context, key string, value []byte) error

Set stores a key-value pair

func (*BadgerStore) SetWithTTL

func (s *BadgerStore) SetWithTTL(_ context.Context, key string, value []byte, ttl time.Duration) error

SetWithTTL stores a key-value pair with a TTL Note: Badger v4 has per-second TTL granularity. TTLs less than 1 second may expire immediately.

func (*BadgerStore) StartMaintenance added in v1.3.0

func (s *BadgerStore) StartMaintenance() error

StartMaintenance starts native Badger maintenance after startup admission. Repeated calls retain the same workers. Closed or imported stores refuse startup.

type CapturedRecordSource added in v1.3.0

type CapturedRecordSource interface {
	RecordSource
	ReadCaptured(context.Context, string, int) (TransferRecord, error)
}

CapturedRecordSource reads an independent immutable capture of a fenced target. Enumerate must visit each record once in strictly ascending key order. ReadCaptured returns the recorded expiry and does not apply current time.

type CompareAndSwapMutation

type CompareAndSwapMutation struct {
	Key           string
	ExpectedValue []byte
	NewValue      []byte
	TTL           time.Duration
}

CompareAndSwapMutation is one conditional write in an atomic mutation set. A nil ExpectedValue requires absence. A nil NewValue deletes the key. A positive TTL replaces the key expiration; zero preserves an existing TTL.

type Config

type Config struct {
	Type   string       `env:"TYPE,default=badger"`
	Badger BadgerConfig `env:",prefix=BADGER_"`
	Valkey ValkeyConfig `env:",prefix=VALKEY_"`
}

Config represents storage configuration

func (Config) RecoveryTargetSHA256 added in v1.3.0

func (c Config) RecoveryTargetSHA256(incarnation string) (string, error)

RecoveryTargetSHA256 binds the selected durable scope without credential material. Native import claims and external writer fencing remain mandatory.

func (*Config) Validate

func (c *Config) Validate() error

Validate validates the storage configuration

type FreshDatabase added in v1.3.0

type FreshDatabase interface {
	IncarnationProvider
	ClaimEmptyDatabase(context.Context, string, string, []byte) error
}

FreshDatabase supports explicit claims on an empty shared database. A matching retry is valid only while the claim remains the sole key.

type ImportActivationInspector added in v1.3.0

type ImportActivationInspector interface {
	CheckActivatedImportAt(context.Context, []byte, ImportReplayPosition, string) error
}

ImportActivationInspector checks an exact native activation without releasing a barrier. Historical completion does not establish current permission or erase a withdrawal.

type ImportActivator added in v1.3.0

type ImportActivator interface {
	ActivateImport(ctx context.Context, claim []byte, decisionSHA256 string) error
}

ImportActivator releases one exact native import claim and retains its receipt. The recovery coordinator must first verify history, fencing, and other stores. A successful activation does not establish those cross-store prerequisites.

type ImportControls added in v1.3.0

type ImportControls struct {
	ActivationPresent     bool
	Activation            []byte
	ReconciliationPresent bool
	Reconciliation        []byte
}

ImportControls holds the exact current native import roots of an unclaimed target. A present root holds its exact persistent bytes. An absent root holds no bytes.

type ImportExpiringRetirer added in v1.3.0

type ImportExpiringRetirer interface {
	ReconcileExpiringImport(context.Context, []byte, int64, string, string, []TransferRecord) (string, error)
}

ImportExpiringRetirer deletes exact captured expiring records under the original import barrier. The native owner must prove that original absolute expiry explains absence. The operation never renews or creates data. Exact durable retries do not delete later state. Callers must validate domain ownership and fence writers.

type ImportInspector added in v1.3.0

type ImportInspector interface {
	InspectImport(context.Context, []byte, ImportReplayPosition, func(TransferRecord) error) error
}

ImportInspector exposes imported records without releasing their startup barrier. Callers must fence writers and discard all yielded records if inspection fails. Native control records remain private. Historical receipts remain visible. Enumeration can repeat identical records. The snapshot owner must deduplicate them.

type ImportPositionInspector added in v1.3.0

type ImportPositionInspector interface {
	CheckImportPosition(context.Context, []byte, ImportReplayPosition) error
}

ImportPositionInspector verifies one exact closed native cursor without enumerating domain records. The check grants no mutation or activation authority.

type ImportReconciler added in v1.3.0

type ImportReconciler interface {
	ReconcileImport(context.Context, []byte, int64, string, string, []CompareAndSwapMutation) (string, error)
}

ImportReconciler commits ordered owner mutations and their receipt under an import barrier. Callers must validate domain transitions and fence all writers. This method grants no admission. Mutations address persistent records only. Exact retries never apply earlier mutations again.

type ImportRecordReader added in v1.3.0

type ImportRecordReader interface {
	ReadImportAt(context.Context, []byte, ImportReplayPosition, string, int) (TransferRecord, error)
}

ImportRecordReader reads one bounded record at an exact closed native replay position. It does not enumerate unrelated records or expose current native import controls. External writer fencing must cover reads and the later owner transaction.

type ImportReplayActivator added in v1.3.0

type ImportReplayActivator interface {
	ActivateImportAt(context.Context, []byte, ImportReplayPosition, string) error
}

ImportReplayActivator releases an import at its exact final replay position. The native transaction checks the cursor and retained history before release. Exact retries retain that position and cannot apply earlier domain changes.

type ImportReplayPosition added in v1.3.0

type ImportReplayPosition struct {
	Sequence      int64
	ReceiptSHA256 string
}

ImportReplayPosition selects the exact native replay boundary to inspect. Zero identifies imported state before the first replay step.

type ImportUnreleasedInspector added in v1.3.0

type ImportUnreleasedInspector interface {
	CheckUnreleasedImport(context.Context, []byte) error
}

ImportUnreleasedInspector checks original closed ownership at any replay cursor. It grants no replay, repair, or activation authority.

type IncarnationProvider added in v1.3.0

type IncarnationProvider interface {
	ObserveIncarnation(context.Context) (string, error)
	BindIncarnation(context.Context, string) (IncarnationStore, error)
}

IncarnationProvider offers process-bound operations for shared recovery. Standalone stores do not need this contract.

type IncarnationStore added in v1.3.0

type IncarnationStore interface {
	ReadWithLifetime(context.Context, string, int) ([]byte, time.Duration, error)
	// CompareAndSwap checks all values and native lease lifetimes before any write.
	// Each live key must occur in mutations with a non-nil ExpectedValue.
	// A live key requires positive native PTTL, including when its value matches.
	CompareAndSwap(context.Context, []CompareAndSwapMutation, ...string) error
}

IncarnationStore checks an externally approved backend identity in every operation. The caller gets approval from its independent recovery authority. Observed identity alone does not grant permission to use a backend.

type KVStore

type KVStore interface {
	// Basic operations
	Get(ctx context.Context, key string) ([]byte, error)
	// GetBounded refuses oversized values without returning partial data.
	GetBounded(ctx context.Context, key string, maxBytes int) ([]byte, error)
	Set(ctx context.Context, key string, value []byte) error
	Delete(ctx context.Context, key string) error
	Exists(ctx context.Context, key string) (bool, error)

	// TTL operations
	SetWithTTL(ctx context.Context, key string, value []byte, ttl time.Duration) error
	GetTTL(ctx context.Context, key string) (time.Duration, error)
	ExpireAt(ctx context.Context, key string, expireAt time.Time) error

	// Atomic operations
	Increment(ctx context.Context, key string, delta int64) (int64, error)
	Decrement(ctx context.Context, key string, delta int64) (int64, error)
	CompareAndSwap(ctx context.Context, key string, old, newValue []byte) error
	CompareAndSwapBatch(ctx context.Context, mutations []CompareAndSwapMutation) error

	// Batch operations
	BatchGet(ctx context.Context, keys []string) (map[string][]byte, error)
	BatchSet(ctx context.Context, items map[string][]byte) error
	BatchDelete(ctx context.Context, keys []string) error
	BatchSetWithTTL(ctx context.Context, items map[string][]byte, ttl time.Duration) error

	// ScanPage preserves native continuation for background recovery.
	ScanPage(ctx context.Context, prefix, cursor string, count int) (KeyPage, error)

	// Scan operations for listing keys
	Scan(ctx context.Context, pattern string, limit int) ([]string, error)
	ScanWithPrefix(ctx context.Context, prefix string, limit int) ([]string, error)

	// Health check and lifecycle
	Ping(ctx context.Context) error
	Close() error
}

KVStore defines the interface for key-value storage operations. All implementations must be thread-safe and support concurrent access.

func Open

func Open(config Config) (KVStore, error)

Open creates the configured KVStore.

func OpenForStartup added in v1.3.0

func OpenForStartup(config Config) (KVStore, error)

OpenForStartup checks native barriers and leaves Badger maintenance stopped. The application must check its other recovery owners before starting maintenance.

func OpenReadOnly added in v1.0.1

func OpenReadOnly(config Config) (KVStore, error)

OpenReadOnly opens configured storage without permitting a logical write.

func OpenValkey

func OpenValkey(config ValkeyConfig) (KVStore, error)

OpenValkey creates a new Valkey-backed KVStore

type KeyPage added in v1.3.0

type KeyPage struct {
	Keys []string
	Next string
}

KeyPage carries one native scan response. An empty Next ends the iteration. Keys can be empty before completion or repeat across pages. The count argument controls work, not the native response size.

Cursors belong to one prefix, store, and process lifetime. Restart from empty after reconnecting to a replacement backend. Consumers must tolerate replay.

type LifetimeReader added in v1.3.0

type LifetimeReader interface {
	ReadWithLifetime(ctx context.Context, key string, maxBytes int) ([]byte, time.Duration, error)
}

LifetimeReader reads a value and its remaining lifetime from one record version. Zero lifetime means no finite expiry evidence. Callers must anchor deadlines before the read. maxBytes bounds the returned payload before copying or transfer.

type LifetimeValue added in v1.3.0

type LifetimeValue struct {
	Value    []byte
	Lifetime time.Duration
	Found    bool
}

LifetimeValue holds one snapshot value and its remaining lifetime. Found distinguishes a missing record from an existing empty value.

type LocalRecoveryTarget added in v1.3.0

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

LocalRecoveryTarget binds an actual durable Badger handle to its selected native directory. It grants no activation, budget authority, or current admission permission.

func OpenLocalRecoveryTarget added in v1.3.0

func OpenLocalRecoveryTarget(ctx context.Context, store KVStore, expectedSHA256 string) (*LocalRecoveryTarget, error)

OpenLocalRecoveryTarget checks an actual writable, persistent, synchronized Badger handle. expectedSHA256 must come from the independently accepted target decision. This function reads native scope without creating files, repairing state, or observing a remote incarnation.

func (*LocalRecoveryTarget) Check added in v1.3.0

func (t *LocalRecoveryTarget) Check(ctx context.Context) error

Check verifies the original handle, durability settings, path, and native directory identity. It neither adopts a replacement directory nor changes any database state.

func (*LocalRecoveryTarget) CheckActivatedImportAt added in v1.3.0

func (t *LocalRecoveryTarget) CheckActivatedImportAt(ctx context.Context, claim []byte, position ImportReplayPosition, decisionSHA256 string) error

CheckActivatedImportAt reads the exact current native activation claim, cursor, and decision. It checks target continuity before and after the native read. It creates or repairs no records.

func (LocalRecoveryTarget) Format added in v1.3.0

func (LocalRecoveryTarget) Format(state fmt.State, _ rune)

Format excludes private target paths and retained identities from diagnostics.

func (*LocalRecoveryTarget) TargetSHA256 added in v1.3.0

func (t *LocalRecoveryTarget) TargetSHA256() string

TargetSHA256 returns the original selected scope digest. Reads alone grant no permission.

type MockStore

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

MockStore is an in-memory implementation of KVStore for testing

func NewMockStore

func NewMockStore() *MockStore

NewMockStore creates a new mock KVStore for testing

func (*MockStore) BatchDelete

func (m *MockStore) BatchDelete(ctx context.Context, keys []string) error

BatchDelete removes multiple keys

func (*MockStore) BatchGet

func (m *MockStore) BatchGet(ctx context.Context, keys []string) (map[string][]byte, error)

BatchGet retrieves multiple values

func (*MockStore) BatchSet

func (m *MockStore) BatchSet(ctx context.Context, items map[string][]byte) error

BatchSet stores multiple values

func (*MockStore) BatchSetWithTTL

func (m *MockStore) BatchSetWithTTL(ctx context.Context, items map[string][]byte, ttl time.Duration) error

BatchSetWithTTL stores multiple values with TTL

func (*MockStore) Close

func (m *MockStore) Close() error

Close closes the store

func (*MockStore) CompareAndSwap

func (m *MockStore) CompareAndSwap(ctx context.Context, key string, old, newValue []byte) error

CompareAndSwap atomically updates a value if it matches the expected value

func (*MockStore) CompareAndSwapBatch

func (m *MockStore) CompareAndSwapBatch(ctx context.Context, mutations []CompareAndSwapMutation) error

CompareAndSwapBatch applies all conditional writes or none of them.

func (*MockStore) Decrement

func (m *MockStore) Decrement(ctx context.Context, key string, delta int64) (int64, error)

Decrement atomically decrements a counter

func (*MockStore) Delete

func (m *MockStore) Delete(ctx context.Context, key string) error

Delete removes a key

func (*MockStore) Exists

func (m *MockStore) Exists(ctx context.Context, key string) (bool, error)

Exists checks if a key exists

func (*MockStore) ExpireAt

func (m *MockStore) ExpireAt(ctx context.Context, key string, expireAt time.Time) error

ExpireAt sets an absolute expiration time for a key

func (*MockStore) Get

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

Get retrieves a value by key

func (*MockStore) GetBounded added in v1.3.0

func (m *MockStore) GetBounded(ctx context.Context, key string, maxBytes int) ([]byte, error)

GetBounded checks the stored size before copying the value.

func (*MockStore) GetTTL

func (m *MockStore) GetTTL(ctx context.Context, key string) (time.Duration, error)

GetTTL returns the remaining TTL for a key

func (*MockStore) Increment

func (m *MockStore) Increment(ctx context.Context, key string, delta int64) (int64, error)

Increment atomically increments a counter

func (*MockStore) Ping

func (m *MockStore) Ping(ctx context.Context) error

Ping checks if the store is healthy

func (*MockStore) Scan

func (m *MockStore) Scan(ctx context.Context, pattern string, limit int) ([]string, error)

Scan returns keys matching a pattern

func (*MockStore) ScanPage added in v1.3.0

func (m *MockStore) ScanPage(ctx context.Context, prefix, cursor string, count int) (KeyPage, error)

ScanPage gives the in-memory test store the ordered cursor contract.

func (*MockStore) ScanWithPrefix

func (m *MockStore) ScanWithPrefix(ctx context.Context, prefix string, limit int) ([]string, error)

ScanWithPrefix returns keys with a specific prefix

func (*MockStore) Set

func (m *MockStore) Set(ctx context.Context, key string, value []byte) error

Set stores a value by key

func (*MockStore) SetWithTTL

func (m *MockStore) SetWithTTL(ctx context.Context, key string, value []byte, ttl time.Duration) error

SetWithTTL stores a value with expiration

type PopulatedImportClaimer added in v1.3.0

type PopulatedImportClaimer interface {
	ObserveImportControls(context.Context) (ImportControls, error)
	ClaimPopulated(context.Context, []byte, ImportControls, CapturedRecordSource) error
}

PopulatedImportClaimer places an import barrier on a populated target and copies no records. The caller must fence all writers from the control observation through activation. The caller retains the observation independently. The claim grants no admission. After the claim, the shared import operations accept the claim as an ordinary claim.

type PubSubClient

type PubSubClient interface {
	// Subscribe to a pattern and handle messages
	Subscribe(pattern string, handler func(channel, message string)) error
	// Publish a message to a channel
	Publish(ctx context.Context, channel string, message string) error
	// Close the pub/sub client
	Close() error
}

PubSubClient defines the interface for pub/sub operations used for cache invalidation

type PubSubProvider

type PubSubProvider interface {
	GetPubSub() PubSubClient
}

PubSubProvider is implemented by storage backends that support pub/sub

type RecordSource added in v1.3.0

type RecordSource interface {
	Enumerate(context.Context, func(TransferRecord) error) error
}

RecordSource enumerates durable records without exposing writes.

type RecordTransfer added in v1.3.0

type RecordTransfer interface {
	RecordSource
	ExpiryResolution() time.Duration
	Claim(context.Context, []byte) error
	Import(context.Context, []byte, TransferRecord) error
}

RecordTransfer serves a stopped deployment's backup and import coordinator. The caller must fence source and target writers throughout the operation. Enumerate can repeat identical records. Import leaves a persistent barrier. These operations do not grant permission to start a recovered deployment.

func OpenRecordTransfer added in v1.3.0

func OpenRecordTransfer(ctx context.Context, store KVStore, identity string) (RecordTransfer, error)

OpenRecordTransfer binds a shared operation to an explicitly selected process. Observing that process does not prove external fencing or recovery approval.

type RecoveryStartupState added in v1.3.0

type RecoveryStartupState struct {
	Activated      bool
	ClaimSHA256    string
	DecisionSHA256 string
	ReceiptSHA256  string
}

RecoveryStartupState reports native activation controls without granting admission. A consistent current cursor cannot substitute for the approved final position.

func InspectRecoveryStartup added in v1.3.0

func InspectRecoveryStartup(ctx context.Context, store KVStore) (RecoveryStartupState, error)

InspectRecoveryStartup checks retained native controls before application maintenance. The complete coordinator must separately verify its sealed decision and selected targets.

type SnapshotSource added in v1.3.0

type SnapshotSource interface {
	RecordSource
	Close() error
}

SnapshotSource owns a native connection that exposes only record enumeration.

func OpenSnapshotSource added in v1.3.0

func OpenSnapshotSource(ctx context.Context, config Config) (SnapshotSource, error)

OpenSnapshotSource opens persistent source storage without exposing mutation methods. Badger opens read-only where supported. Windows needs an exclusive native open that can perform engine recovery; application maintenance stays disabled. Shared enumeration binds the observed backend incarnation. Observation does not prove external writer fencing.

func OpenUnprefixedValkeySnapshotSource added in v1.3.0

func OpenUnprefixedValkeySnapshotSource(ctx context.Context, config ValkeyConfig) (SnapshotSource, error)

OpenUnprefixedValkeySnapshotSource captures a dedicated database for explicit namespace migration. The operator must stop and fence every source writer. The connection exposes enumeration only and binds the observed process identity. Ordinary gateway storage never selects this layout.

type TimeBoundStore added in v1.3.0

type TimeBoundStore interface {
	ReadWithLifetime(context.Context, string, int) ([]byte, time.Duration, error)
	// ReadBatchWithLifetime returns one snapshot in key order, with no partial results on error.
	// It accepts at most sixteen unique keys and one MiB of possible payload.
	// The byte bound applies per value. The operation grants no write permission.
	ReadBatchWithLifetime(context.Context, []string, int) ([]LifetimeValue, error)
	AuthorityTime(context.Context) (time.Time, error)
	CompareAndSwapInWindow(context.Context, []CompareAndSwapMutation, TimeWindow) error
}

TimeBoundStore uses its own clock to guard conditional writes. Reading AuthorityTime does not authorize a later write. The write checks time again in the same transaction that checks values. Shared implementations must also enforce their approved backend incarnation in each operation.

type TimeWindow added in v1.3.0

type TimeWindow struct {
	Start time.Time
	End   time.Time
}

TimeWindow is a half-open interval in the storage authority's UTC clock. Boundaries have whole-second precision. The zero value imposes no time check.

type TransferRecord added in v1.3.0

type TransferRecord struct {
	Key             string
	Value           []byte
	ExpiresAtMillis int64
}

TransferRecord preserves bytes and absolute expiry. Zero expiry means persistent.

func (TransferRecord) Validate added in v1.3.0

func (r TransferRecord) Validate() error

Validate checks portable representation without changing a record.

type ValkeyConfig

type ValkeyConfig struct {
	DeploymentID   string
	URL            string `env:"URL,default=redis://localhost:6379"`
	Username       string
	CAFile         string
	AllowInsecure  bool
	DialTimeout    time.Duration
	MaxConnections int
	IdleTimeout    time.Duration
	MinIdleConns   int           `env:"MIN_IDLE_CONNS,default=10"`
	ReadTimeout    time.Duration `env:"READ_TIMEOUT,default=3s"`
	WriteTimeout   time.Duration `env:"WRITE_TIMEOUT,default=3s"`
	Password       string        `env:"PASSWORD"`
	DB             int           `env:"DB,default=0"`
	ClusterMode    bool          `env:"CLUSTER_MODE,default=false"`
}

ValkeyConfig represents Valkey/Redis-specific configuration

func (ValkeyConfig) ValidateConnection added in v1.3.0

func (c ValkeyConfig) ValidateConnection() error

ValidateConnection checks the selected transport without reading files or dialing.

type ValkeyPubSub

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

ValkeyPubSub implements PubSubClient using Valkey pub/sub

func (*ValkeyPubSub) Close

func (v *ValkeyPubSub) Close() error

Close closes all subscriptions

func (*ValkeyPubSub) Publish

func (v *ValkeyPubSub) Publish(ctx context.Context, channel string, message string) error

Publish publishes a message to a channel

func (*ValkeyPubSub) Subscribe

func (v *ValkeyPubSub) Subscribe(pattern string, handler func(channel, message string)) error

Subscribe subscribes to a pattern and handles messages

type ValkeyStore

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

ValkeyStore implements KVStore interface using Valkey

func (*ValkeyStore) BatchDelete

func (v *ValkeyStore) BatchDelete(ctx context.Context, keys []string) error

BatchDelete removes multiple keys

func (*ValkeyStore) BatchGet

func (v *ValkeyStore) BatchGet(ctx context.Context, keys []string) (map[string][]byte, error)

BatchGet retrieves multiple values

func (*ValkeyStore) BatchSet

func (v *ValkeyStore) BatchSet(ctx context.Context, items map[string][]byte) error

BatchSet stores multiple key-value pairs

func (*ValkeyStore) BatchSetWithTTL

func (v *ValkeyStore) BatchSetWithTTL(ctx context.Context, items map[string][]byte, ttl time.Duration) error

BatchSetWithTTL stores multiple key-value pairs with TTL

func (*ValkeyStore) BindIncarnation added in v1.3.0

func (v *ValkeyStore) BindIncarnation(ctx context.Context, approved string) (IncarnationStore, error)

BindIncarnation checks the approved identity and retains it for each operation.

func (*ValkeyStore) ClaimEmptyDatabase added in v1.3.0

func (v *ValkeyStore) ClaimEmptyDatabase(ctx context.Context, identity, key string, claim []byte) error

ClaimEmptyDatabase binds a fresh database to one initialization operation. The identity check, empty-state check, and claim write form one native operation.

func (*ValkeyStore) Close

func (v *ValkeyStore) Close() error

Close closes the connection

func (*ValkeyStore) CompareAndSwap

func (v *ValkeyStore) CompareAndSwap(ctx context.Context, key string, old, newValue []byte) error

CompareAndSwap atomically updates a value if it matches the old value

func (*ValkeyStore) CompareAndSwapBatch

func (v *ValkeyStore) CompareAndSwapBatch(ctx context.Context, mutations []CompareAndSwapMutation) error

CompareAndSwapBatch applies all conditional writes or none of them.

func (*ValkeyStore) Decrement

func (v *ValkeyStore) Decrement(ctx context.Context, key string, delta int64) (int64, error)

Decrement atomically decrements a value

func (*ValkeyStore) Delete

func (v *ValkeyStore) Delete(ctx context.Context, key string) error

Delete removes a key

func (*ValkeyStore) Exists

func (v *ValkeyStore) Exists(ctx context.Context, key string) (bool, error)

Exists checks if a key exists

func (*ValkeyStore) ExpireAt

func (v *ValkeyStore) ExpireAt(ctx context.Context, key string, expireAt time.Time) error

ExpireAt sets expiration time for a key

func (*ValkeyStore) Get

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

Get retrieves a value by key

func (*ValkeyStore) GetBounded added in v1.3.0

func (v *ValkeyStore) GetBounded(ctx context.Context, key string, maxBytes int) ([]byte, error)

GetBounded atomically checks length before returning a payload from Valkey.

func (*ValkeyStore) GetPubSub

func (v *ValkeyStore) GetPubSub() PubSubClient

GetPubSub returns the pub/sub client for cache invalidation

func (*ValkeyStore) GetTTL

func (v *ValkeyStore) GetTTL(ctx context.Context, key string) (time.Duration, error)

GetTTL returns the remaining TTL for a key

func (*ValkeyStore) Increment

func (v *ValkeyStore) Increment(ctx context.Context, key string, delta int64) (int64, error)

Increment atomically increments a value

func (*ValkeyStore) ObserveIncarnation added in v1.3.0

func (v *ValkeyStore) ObserveIncarnation(ctx context.Context) (string, error)

ObserveIncarnation reads the serving process and replication history identity. The initial recovery recipe supports one controlled primary, without cluster failover.

func (*ValkeyStore) Ping

func (v *ValkeyStore) Ping(ctx context.Context) error

Ping checks if the connection is alive

func (*ValkeyStore) ReadWithLifetime added in v1.3.0

func (v *ValkeyStore) ReadWithLifetime(ctx context.Context, key string, maxBytes int) ([]byte, time.Duration, error)

ReadWithLifetime binds a bounded value and its PTTL in one server operation.

func (*ValkeyStore) Scan

func (v *ValkeyStore) Scan(ctx context.Context, pattern string, limit int) ([]string, error)

Scan returns keys matching a pattern

func (*ValkeyStore) ScanPage added in v1.3.0

func (v *ValkeyStore) ScanPage(ctx context.Context, prefix, cursor string, count int) (KeyPage, error)

ScanPage reads one native Valkey cursor step without truncating its response.

func (*ValkeyStore) ScanWithPrefix

func (v *ValkeyStore) ScanWithPrefix(ctx context.Context, prefix string, limit int) ([]string, error)

ScanWithPrefix returns keys with a specific prefix

func (*ValkeyStore) Set

func (v *ValkeyStore) Set(ctx context.Context, key string, value []byte) error

Set stores a value with a key

func (*ValkeyStore) SetWithTTL

func (v *ValkeyStore) SetWithTTL(ctx context.Context, key string, value []byte, ttl time.Duration) error

SetWithTTL stores a value with expiration

Jump to

Keyboard shortcuts

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