Skill 持久化后端 behaviour。
把 CMDCSkillEngine.Store 的存储细节从 GenServer 主体里解耦,
让同一个 GenServer 可以在不同场景下挂载不同后端:
CMDCSkillEngine.Store.Backend.ETS(默认)— 进程内、重启即丢,零依赖CMDCSkillEngine.Store.Backend.SQLite— 文件持久化,跨重启保留版本 DAG 与计数器
v0.3 多租户切片
v0.3 起所有按 skill_id 检索 / 写入计数的 callback 加 scope 参数(String.t())。
- 单租户私有化部署不变:Store 上层默认
scope = "global" - SaaS 多租户:每个租户独立 scope(如
tenant_a_uuid),同一 backend 存储多个租户的数据但相互隔离 - 不同 scope 下允许出现同名 / 同 skill_id 的记录(互不可见)
Breaking change(v0.3.0 vs v0.2.x):所有 callback 签名加
scope参数(save_record/2例外 —— scope 由record.scope字段携带)。 第三方 backend 实现需要适配。
约定
- 所有回调接收/返回
state,由 Store 持有、不可见于外部调用方 - 回调 不得抛异常,失败返回
{:error, reason} save_record/2负责 upsert(若(record.scope, record.skill_id)已存在则覆盖)update_counters/4必须原子性保证同一条 record 的四项计数递增是一致的list_all/2返回该 scope 下全部 record,由Store客户端过滤is_active, 让后端实现尽量简单reset/1清空所有 scope(仅测试用)
自定义后端
defmodule MyApp.PGBackend do
@behaviour CMDCSkillEngine.Store.Backend
@impl true
def init(opts), do: ...
@impl true
def get_record(state, scope, skill_id), do: ...
# ...
end然后在 CMDCSkillEngine.Store.start_link/1 传入 backend: MyApp.PGBackend。
Summary
Types
Callbacks
@callback get_record(state(), scope(), String.t()) :: {:ok, CMDCSkillEngine.Types.SkillRecord.t()} | {:error, :not_found}
@callback list_all(state(), scope()) :: {:ok, [CMDCSkillEngine.Types.SkillRecord.t()]} | {:error, term()}
@callback save_record(state(), CMDCSkillEngine.Types.SkillRecord.t()) :: {:ok, CMDCSkillEngine.Types.SkillRecord.t()} | {:error, term()}
@callback terminate(state()) :: :ok