Store Primer
Store Primer
Section titled “Store Primer”当你已经通过 稳定根 确认改动仍属于稳定表面,但问题进一步收窄到持久化原语与存储 contract 时,就打开这一页。
store 拥有基础持久化抽象。只有当工作应继续停留在拓扑较重的数据特性、tenant-aware 实现,以及 HTTP-facing 文件或缓存行为之下时,它才是正确归属。
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)什么时候从这里开始
Section titled “什么时候从这里开始”- 你正在定义稳定存储 contract 或基础持久化 helper
- 工作仍然停留在 provider-specific topology 决策之下
- 改动属于 file、KV、cache、idempotency 或 DB helper primitive
什么时候不该从这里开始
Section titled “什么时候不该从这里开始”- 工作已经变成 tenant-aware storage policy 或 tenant-aware adapter
- 任务引入 distributed engine 行为、signed URL 或 metadata-manager ownership
- 真正的问题是 upload/download HTTP transport,或由 gateway 拥有的 response caching
当前仓库里先读哪些文件
Section titled “当前仓库里先读哪些文件”store/module.yamlstore/*下的目标包specs/repo.yaml
更具体的归属例子
Section titled “更具体的归属例子”这些工作适合留在 store | 一旦变成这些问题就应移出 |
|---|---|
store/file 接口、共享文件类型与错误 | x/data/file 中的 tenant-aware file backend 与 metadata persistence,或 x/fileapi 中的 HTTP upload/download endpoint |
store/kv 的 file-backed KV CRUD、TTL-aware helper、key scan 与 basic stats | x/data/kvengine 中的 WAL、snapshot、serializer selection、compression 与 shard tuning |
store/idempotency 的 record、status、error 与最小 Store 接口 | x/data/idempotency 中的 SQL/KV provider、SQL dialect policy 与 duplicate-key handling |
store/cache 的基础 cache primitive | x/data/cache 中的 distributed cache engine、x/gateway/cache 中的 HTTP response caching,以及 x/tenant/store/cache 中的 tenant-aware cache adapter |
store/db 中依赖调用方 context.Context 的 helper 执行 | x/observability/dbinsights 中的 DB analytics 与 slow-query inspection |
store/kv — 基于文件的 KV 存储
Section titled “store/kv — 基于文件的 KV 存储”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 并发安全。适合短期应用状态,如 JWT 密钥、会话和限流计数器。高吞吐或分布式场景请用 x/data/cache。
store/cache — 内存缓存
Section titled “store/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")Cache 接口允许替换实现:
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 助手
Section titled “store/db — SQL 助手”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 — 精确一次操作保障
Section titled “store/idempotency — 精确一次操作保障”import "github.com/spcent/plumego/store/idempotency"
// 实现 Store 接口(或使用 x/data/idempotency 获取持久化 provider)var idem idempotency.Store
rec, err := idem.Get(ctx, idempotencyKey)if err == nil && rec.Status == idempotency.StatusCompleted { // 重放已存储的响应 return rec.Response, nil}
result, err := doExpensiveOperation(ctx, req)if err != nil { return nil, err }
idem.Set(ctx, idempotencyKey, idempotency.Record{ Status: idempotency.StatusCompleted, Response: result,})为什么单独写这一页
Section titled “为什么单独写这一页”store 看起来容易变成一个很宽的目录,因为持久化会碰到很多能力层。当前仓库的真实规则,是刻意把它压窄:稳定持久化 contract 留在这里,而 provider-heavy、topology-heavy、tenant-aware、HTTP-facing 行为则向外移动。