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

persistence

package
v1.9.0 Latest Latest
Warning

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

Go to latest
Published: Oct 27, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package persistence provides a pluggable persistence layer for Gopherstack services. Services implement the Persistable interface to participate in snapshot-based state serialisation. The Store interface abstracts the underlying storage medium.

Index

Constants

This section is empty.

Variables

View Source
var ErrInvalidMode = errors.New("persistence: invalid setting")

ErrInvalidMode flags a bad mode or service-mode entry.

View Source
var ErrKeyNotFound = errors.New("key not found")

ErrKeyNotFound is returned by Load when the requested key does not exist.

Functions

func MarshalSnapshot

func MarshalSnapshot(ctx context.Context, service string, v any) []byte

MarshalSnapshot serialises v to JSON for a service-state snapshot.

On success it emits a debug record (silent at the default info level) and returns the encoded bytes. On failure it logs a warning and returns nil, matching the Persistable.Snapshot contract (a nil snapshot is skipped by the persistence Manager). Centralising this here keeps every backend's Snapshot method consistent and gives the ctx parameter a purpose.

func UnmarshalSnapshot

func UnmarshalSnapshot(ctx context.Context, service string, data []byte, v any) error

UnmarshalSnapshot decodes a service-state snapshot from data into v.

On success it emits a debug record; on failure it logs a warning and returns the decode error. Centralising this here keeps every backend's Restore method consistent and gives the ctx parameter a purpose.

Types

type FileStore

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

FileStore persists blobs as JSON files on the local file system. Data is stored at {baseDir}/{service}/{key}.json.

func NewFileStore

func NewFileStore(baseDir string) (*FileStore, error)

NewFileStore creates a FileStore rooted at baseDir. The directory is created (with parents) if it does not exist.

func (*FileStore) Delete

func (f *FileStore) Delete(service, key string) error

Delete removes {baseDir}/{service}/{key}.json. It is not an error if the file does not exist.

func (*FileStore) ListKeys

func (f *FileStore) ListKeys(service string) ([]string, error)

ListKeys returns the base names (without .json extension) of all files under {baseDir}/{service}/. Temp files created during atomic saves are excluded.

func (*FileStore) Load

func (f *FileStore) Load(service, key string) ([]byte, error)

Load reads data from {baseDir}/{service}/{key}.json. Returns ErrKeyNotFound when the file does not exist.

func (*FileStore) Save

func (f *FileStore) Save(service, key string, data []byte) error

Save writes data to {baseDir}/{service}/{key}.json.

type LoadStrategy

type LoadStrategy string

LoadStrategy selects when persisted state is restored.

const (
	// LoadStartup restores every service at startup (default).
	LoadStartup LoadStrategy = "startup"
	// LoadRequest restores a service lazily on its first request.
	LoadRequest LoadStrategy = "request"
	// LoadManual restores only through an explicit LoadNow.
	LoadManual LoadStrategy = "manual"
)

func ParseLoadStrategy

func ParseLoadStrategy(s string) (LoadStrategy, error)

ParseLoadStrategy validates a load strategy case-insensitively; empty means LoadStartup.

type Manager

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

Manager coordinates persistence for a set of named service backends. It restores state on startup and performs debounced async saves on mutation.

func NewManager

func NewManager(ctx context.Context, store Store) *Manager

NewManager creates a Manager backed by the given Store. The supplied context is the process lifecycle context; it carries the root logger and is used for the debounced async save path (Notify -> AfterFunc), which has no per-request or per-call context of its own. A nil ctx falls back to context.Background().

func (*Manager) EnsureLoaded

func (m *Manager) EnsureLoaded(ctx context.Context, name string)

EnsureLoaded restores the named service on first use under LoadRequest. It is a no-op under any other strategy, for unknown services and once loaded.

func (*Manager) ExportAll

func (m *Manager) ExportAll() map[string][]byte

ExportAll captures an in-memory snapshot from every registered service and returns the results as a map of service name → raw snapshot bytes. Services that return an empty snapshot (nil or zero-length) are omitted from the map. The store is not written; this is a pure read of current in-memory state.

func (*Manager) FlushDirty

func (m *Manager) FlushDirty(ctx context.Context) int

FlushDirty saves every hybrid-mode service whose state changed since its last flush and returns how many were written. Unchanged services are skipped.

func (*Manager) ImportAll

func (m *Manager) ImportAll(ctx context.Context, snapshots map[string][]byte) error

ImportAll restores state for every service whose name appears in snapshots. Services not registered with the manager are warned and skipped. Restore errors are collected; all services in the map are attempted regardless of earlier failures. Returns a combined error if any restore failed, nil otherwise.

func (*Manager) LoadNow

func (m *Manager) LoadNow(ctx context.Context) int

LoadNow restores every enabled service from the store regardless of the load strategy and returns how many snapshots were applied.

func (*Manager) LoadStrategy

func (m *Manager) LoadStrategy() LoadStrategy

LoadStrategy reports the configured load strategy (LoadStartup when unset).

func (*Manager) Notify

func (m *Manager) Notify(name string)

Notify schedules a debounced save for the named service. If no service with that name is registered the call is a no-op.

Each Notify bumps a per-entry generation counter, stops any pending timer, and starts a fresh AfterFunc that captures the new generation. The save callback only proceeds if the generation it was created with is still the current one. This prevents two pathological cases:

  1. A previously-fired AfterFunc whose closure captured an old generation racing with a newer Notify (the old fire is skipped by the gen check).
  2. Notify being called multiple times during the debounce window: only the most recent timer's closure will pass the generation check and perform the save.

Earlier versions of this function captured the generation only on the first Notify and reused the same AfterFunc via Reset, which caused the captured generation to fall stale and the eventual save to be skipped indefinitely after a second Notify.

func (*Manager) Register

func (m *Manager) Register(name string, p Persistable)

Register associates a named Persistable with the manager. It must be called before RestoreAll or Notify.

func (*Manager) RestoreAll

func (m *Manager) RestoreAll(ctx context.Context)

RestoreAll loads and restores snapshots at startup. Under LoadRequest and LoadManual only internal "system-*" entries are restored here; services wait for EnsureLoaded or LoadNow. Errors for individual services are logged but do not abort restoration of others.

func (*Manager) SaveAll

func (m *Manager) SaveAll(ctx context.Context)

SaveAll persists all enabled backends except ModeManual ones; unloaded services under a lazy load strategy are skipped so they keep their stored state. Pending debounce timers are stopped. Intended for graceful shutdown.

func (*Manager) SaveNow

func (m *Manager) SaveNow(ctx context.Context) int

SaveNow persists every enabled service immediately, whatever its mode, and returns how many snapshots were written.

func (*Manager) SetLoadStrategy

func (m *Manager) SetLoadStrategy(l LoadStrategy)

SetLoadStrategy selects when RestoreAll and EnsureLoaded restore state. Call before RestoreAll.

func (*Manager) SetPolicy

func (m *Manager) SetPolicy(p Policy)

SetPolicy installs the per-service persistence policy. Call before RestoreAll.

func (*Manager) StartFlusher

func (m *Manager) StartFlusher(ctx context.Context, interval time.Duration) func()

StartFlusher runs FlushDirty every interval until the returned stop function is called; stop waits for the loop.

type Mode

type Mode string

Mode selects how a service's state reaches disk.

const (
	// ModeSnapshot debounces a full snapshot after each mutating request (default).
	ModeSnapshot Mode = "snapshot"
	// ModeHybrid flushes only services changed since the last flush, on an interval and at shutdown.
	ModeHybrid Mode = "hybrid"
	// ModeShutdown saves only at graceful shutdown.
	ModeShutdown Mode = "shutdown"
	// ModeManual saves only through an explicit SaveNow, never at shutdown.
	ModeManual Mode = "manual"
)

func ParseMode

func ParseMode(s string) (Mode, error)

ParseMode validates a mode name case-insensitively; empty means ModeSnapshot.

type NullStore

type NullStore struct{}

NullStore is a no-op Store that preserves zero-persistence behaviour. It is the default when PERSIST is not set.

func (NullStore) Delete

func (NullStore) Delete(_, _ string) error

Delete is a no-op.

func (NullStore) ListKeys

func (NullStore) ListKeys(_ string) ([]string, error)

ListKeys always returns an empty slice.

func (NullStore) Load

func (NullStore) Load(_, _ string) ([]byte, error)

Load always returns ErrKeyNotFound.

func (NullStore) Save

func (NullStore) Save(_, _ string, _ []byte) error

Save is a no-op.

type Persistable

type Persistable interface {
	Snapshot(ctx context.Context) []byte
	Restore(ctx context.Context, data []byte) error
}

Persistable is an optional interface that service backends may implement to participate in Gopherstack's snapshot-based persistence.

Snapshot returns an opaque JSON blob representing current backend state. Restore initialises backend state from a previously-captured blob.

Both methods receive a context so they log through the context-aware logger (logger.Load(ctx)) — the persistence Manager threads a context tagged worker=<service>-persistence — instead of the global slog.Default().

type Policy

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

Policy decides, per service, whether it is persisted and in which mode. The zero value persists every service in ModeSnapshot.

func NewPolicy

func NewPolicy(def Mode, include, exclude, overrides []string) (Policy, error)

NewPolicy builds a Policy. include (when non-empty) is an allow-list, exclude is applied after it, and overrides are "service=mode" entries. Names match case-insensitively.

func (Policy) ModeFor

func (p Policy) ModeFor(name string) (Mode, bool)

ModeFor reports the mode for a service and whether it is persisted at all. Internal "system-*" entries are always persisted in ModeSnapshot (saved at shutdown).

func (Policy) Uses

func (p Policy) Uses(mode Mode) bool

Uses reports whether any service can resolve to mode under this policy.

type Store

type Store interface {
	// Save persists data for the given service and key.
	Save(service, key string, data []byte) error

	// Load retrieves data for the given service and key.
	// Returns ErrKeyNotFound when the key does not exist.
	Load(service, key string) ([]byte, error)

	// Delete removes the data for the given service and key.
	// It is not an error to delete a non-existent key.
	Delete(service, key string) error

	// ListKeys returns all keys stored for the given service.
	ListKeys(service string) ([]string, error)
}

Store is the persistence back-end abstraction. Save/Load/Delete operate on named blobs identified by (service, key). ListKeys returns all keys stored for a service.

Jump to

Keyboard shortcuts

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