You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
5.8 KiB
5.8 KiB
popiartServer 薄膜持久化设计
这份文档描述当前的轻量化持久化方案。目标不是把 popiartServer
做成第二个模型平台,而是让它只保存 PopiArt 自己的产品语义。
设计原则
PopiNewAPI 已经负责:
- 上游 provider key
- 模型请求
- 原始用量
- 一部分任务型能力的 task 状态
所以 popiartServer 不再重复持有:
- provider 侧原始产物数据库
- 本地 job_logs table
它当前保留五类本地状态:
sessionsjobs(更准确说是 job refs)artifactsmedia_recordsskill_routesmedia blobs
当前关系
session -> jobs
jobs -> result_refs_json
jobs -> artifacts
artifacts -> media_records
project -> skill_routes
media -> local files
其中:
session保存 PopiArt 登录态和对应的PopiNewAPI tokenjobs保存 skill 语义、用户归属、上游引用和同步结果引用result_refs_json仍作为 job 级兼容引用保留artifacts保存用户可查询的 artifact 元数据与溯源快照media_records保存稳定 URL 所需的 media 元数据skill_routes保存项目级路由覆盖media blobs保存真实文件内容
存储位置
默认配置来自这些环境变量:
POPIART_DATA_DIRPOPIART_SQLITE_PATHPOPIART_SESSION_SECRET
默认值:
POPIART_DATA_DIR=./data
POPIART_SQLITE_PATH=./data/popiart.db
Repository 接口
代码位置:
internal/server/repository.go
当前 repository 分成五层:
SessionRepositoryJobRepositoryRouteRepositoryArtifactRepositoryMediaRepository
本地开发版没有对象存储依赖,但现在有一个轻量 media 存储层:
- blob 文件保存在
POPIART_DATA_DIR/media/blobs/ - 兼容期 metadata JSON 保存在
POPIART_DATA_DIR/media/meta/ - SQLite 中的
media_records/artifacts是当前主元数据存储
SQLite Schema
sessions
CREATE TABLE sessions (
session_id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
user_json TEXT NOT NULL,
token_enc BLOB NOT NULL,
token_masked TEXT NOT NULL,
created_at TEXT NOT NULL,
expires_at TEXT NOT NULL
);
jobs
CREATE TABLE jobs (
job_id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
session_id TEXT,
project_id TEXT,
skill_id TEXT,
route_key TEXT,
model_id TEXT,
exec_mode TEXT NOT NULL,
newapi_task_id TEXT,
status TEXT NOT NULL,
input_json TEXT NOT NULL,
result_refs_json TEXT,
usage_json TEXT,
error_json TEXT,
idempotency_key TEXT,
created_at TEXT NOT NULL,
started_at TEXT,
finished_at TEXT,
UNIQUE(user_id, idempotency_key)
);
media_records
CREATE TABLE media_records (
media_id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
artifact_id TEXT,
project_id TEXT,
filename TEXT NOT NULL,
content_type TEXT NOT NULL,
size_bytes INTEGER NOT NULL,
created_at TEXT NOT NULL,
url TEXT NOT NULL,
visibility TEXT,
sha256 TEXT,
local_path TEXT NOT NULL
);
artifacts
CREATE TABLE artifacts (
artifact_id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
job_id TEXT NOT NULL,
project_id TEXT,
result_index INTEGER NOT NULL,
media_id TEXT,
filename TEXT NOT NULL,
content_type TEXT NOT NULL,
size_bytes INTEGER NOT NULL,
created_at TEXT NOT NULL,
expires_at TEXT,
visibility TEXT,
sha256 TEXT,
storage_status TEXT,
source_skill_id TEXT,
source_model_id TEXT,
source_route_key TEXT,
source_input_json TEXT,
prompt_text TEXT,
usage_json TEXT,
ref_json TEXT NOT NULL
);
skill_routes
CREATE TABLE skill_routes (
route_key TEXT NOT NULL,
scope_key TEXT NOT NULL,
model_id TEXT NOT NULL,
options_json TEXT,
updated_at TEXT NOT NULL,
PRIMARY KEY(route_key, scope_key)
);
运行时流程
登录
- CLI 把
PopiNewAPI token交给popiartServer popiartServer用/v1/models验证 token- server 签发本地
sess_xxx - 本地 session 持久化到 SQLite,重启后仍可继续使用
创建 job
- server 解析
skill_id -> route_key -> model_id jobs表写入一条本地 job ref- goroutine 开始向
PopiNewAPI发请求
完成同步结果 job
- 如果
PopiNewAPI直接返回同步结果,例如b64_json或url popiartServer会先把结果 re-host 到本地 media store- 再把本地
local_path + media_id + stable url写进result_refs_json - 同时 upsert 到
media_records和artifacts GET /jobs/:id/artifacts/GET /v1/artifacts优先从 SQLite 读取
拉取 artifact
- server 从
artifact_id反推出job_id + result index - 优先读取
artifacts表中的ref_json - 如果 SQLite 尚未命中,则从
jobs.result_refs_json回填并重试 - 如果是本地
local_path,直接读取本地文件并流式返回 - 如果是旧的
data_url,直接解码并流式返回 - 如果是旧的远端
url,server 代理下载并返回
读取 media
POST /v1/media/upload会把本地文件写入media/blobs/- server 同时写 SQLite
media_records,并保留兼容 JSON meta GET /v1/media/:id优先读 SQLite,JSON 作为 fallbackGET /v1/media/:id/content从local_path读取内容
当前边界
- server 重启后:
session会保留job ref会保留artifact通过 SQLite 主路径继续可读- 旧数据仍可通过
result_refs_json/ JSON meta 懒回填 - 新
media文件和 metadata 会继续可读
- 已存在的
pending/runningjob 仍然不会自动恢复执行 - 视频类
upstream_task路径只预留了字段,后续再接PopiNewAPItask 查询 - 旧数据里仍可能存在
data_url或上游url - 旧
media/meta/*.json仍保留作为兼容层,不再是主元数据来源 - 新写入路径优先落 SQLite + 本地 media store,从而给 artifact 补出稳定
url