Store Primer
Store Primer
Section titled “Store Primer”Open this page after Stable Roots when the change still belongs to the stable surface, but the real question has narrowed to persistence primitives and storage contracts.
store owns base persistence abstractions. It is the right home when the work should stay below topology-heavy data features, tenant-aware implementations, and HTTP-facing file or cache behavior.
var repo store.Repository[User] = newUserRepo(db)user, err := repo.Get(ctx, userID)if err != nil { _ = contract.WriteError(w, r, contract.NewErrorBuilder(). Type(contract.TypeNotFound). Message("user not found"). Build()) return}_ = contract.WriteResponse(w, r, http.StatusOK, user, nil)Start here when
Section titled “Start here when”- you are defining stable storage contracts or basic persistence helpers
- the work stays below provider-specific topology decisions
- the change belongs to file, KV, cache, idempotency, or DB helper primitives
Do not start here when
Section titled “Do not start here when”- the work is tenant-aware storage policy or tenant-aware adapters
- the task introduces distributed engine behavior, signed URLs, or metadata-manager ownership
- the real problem is upload/download HTTP transport or gateway-owned response caching
First files to read in the current repository
Section titled “First files to read in the current repository”store/module.yaml- the target package under
store/* specs/repo.yaml
Concrete ownership examples
Section titled “Concrete ownership examples”Keep it in store when the work is about | Move out when the work becomes |
|---|---|
store/file interfaces, shared file types, and errors | tenant-aware file backends and metadata persistence in x/data/file, or HTTP upload/download endpoints in x/fileapi |
store/kv file-backed KV CRUD, TTL-aware helpers, key scans, and basic stats | WAL, snapshots, serializer selection, compression, and shard tuning in x/data/kvengine |
store/idempotency records, statuses, errors, and the minimal Store interface | durable SQL or KV providers, SQL dialect policy, and duplicate-key handling in x/data/idempotency |
store/cache base cache primitives | distributed cache engines in x/data/cache, HTTP response caching in x/gateway/cache, and tenant-aware cache adapters in x/tenant/store/cache |
store/db helper execution with caller-owned context.Context | DB analytics and slow-query inspection in x/observability/dbinsights |
store/kv — file-backed key-value store
Section titled “store/kv — file-backed key-value store”import kvstore "github.com/spcent/plumego/store/kv"
kv, err := kvstore.NewKVStore(kvstore.Options{ DataDir: "/var/lib/myapp/kv",})if err != nil { ... }defer kv.Close()
kv.Set("session:abc", []byte(`{"user":"alice"}`), 30*time.Minute)
data, err := kv.Get("session:abc")
kv.Delete("session:abc")
exists := kv.Exists("session:abc")KVStore is safe for concurrent use. Use it for short-lived application state such as JWT keys, sessions, and rate-limit counters. For distributed or high-throughput scenarios, see x/data/cache.
store/cache — in-memory cache
Section titled “store/cache — in-memory cache”import "github.com/spcent/plumego/store/cache"
c, err := cache.NewMemoryCacheWithConfig(cache.DefaultConfig())if err != nil { ... }
c.Set(ctx, "user:42", jsonBytes, 5*time.Minute)
data, err := c.Get(ctx, "user:42")The Cache interface lets you swap implementations:
type Cache interface { Get(ctx context.Context, key string) ([]byte, error) Set(ctx context.Context, key string, value []byte, ttl time.Duration) error Delete(ctx context.Context, key string) error Exists(ctx context.Context, key string) (bool, error) Close() error}store/db — SQL helpers
Section titled “store/db — SQL helpers”import "github.com/spcent/plumego/store/db"
sqlDB, err := db.Open(db.DefaultConfig("postgres", dsn))if err != nil { ... }
rows, err := db.QueryContext(ctx, sqlDB, "SELECT id, name FROM users WHERE active = $1", true)
result, err := db.ExecContext(ctx, sqlDB, "UPDATE users SET name = $1 WHERE id = $2", name, id)
err = db.WithTransaction(ctx, sqlDB, nil, func(tx *sql.Tx) error { _, err := tx.ExecContext(ctx, "INSERT INTO orders ...", ...) return err})store/idempotency — exactly-once operation guard
Section titled “store/idempotency — exactly-once operation guard”import "github.com/spcent/plumego/store/idempotency"
// Implement the Store interface (or use x/data/idempotency for a durable provider)var idem idempotency.Store
rec, err := idem.Get(ctx, idempotencyKey)if err == nil && rec.Status == idempotency.StatusCompleted { // replay the stored response return rec.Response, nil}
// Process the operationresult, err := doExpensiveOperation(ctx, req)if err != nil { return nil, err }
idem.Set(ctx, idempotencyKey, idempotency.Record{ Status: idempotency.StatusCompleted, Response: result,})Why this primer exists
Section titled “Why this primer exists”store looks broad because persistence touches many capabilities. The current repository narrows it deliberately: keep the stable persistence contracts here, and move provider-heavy, topology-heavy, tenant-aware, and HTTP-facing behavior outward.