持久化存储
ctx.store 是本模块专属的持久化存储,自动加密落盘,与其他模块完全隔离。文件路径、数据库连接、密钥都由宿主管理,你碰不到也不用碰。
ctx.store 在 setup() 阶段就可用,不必等到 on_enable()。耗时的批量读写建议放到启用之后。
KV 层(日常推荐)
| 方法 | 说明 |
|---|---|
get(key, default=None) | 读取并自动反序列化,不存在返回 default |
set(key, value) | 写入(自动序列化 + 落盘),支持 str/int/float/bool/None/list/dict |
set_many(mapping) | 批量写入,只落盘一次,高频写入用这个 |
get_many(prefix="") | 按前缀批量读取,返回 {key: value} |
delete(key) | 删除,返回是否删除成功 |
keys(prefix="") | 列出键(可前缀过滤) |
size() | 键数量 |
exists() | 本模块是否已有存储数据 |
ctx.store.set("token", "abc123")
ctx.store.set("devices", [{"serial": "A"}, {"serial": "B"}])
ctx.store.get("token")
ctx.store.get_many("dev") # 前缀匹配
ctx.store.delete("token")SQL 层(需要表结构时)
| 方法 | 说明 |
|---|---|
query(sql, params=()) | 只读,返回 [{列名: 值}];只允许 SELECT / PRAGMA / EXPLAIN |
execute(sql, params=()) | 写语句(INSERT / UPDATE / DELETE / CREATE 等),自动提交,返回受影响行数 |
transaction() | 上下文管理器,块内多次写入合并为一次落盘 |
# 建表
ctx.store.execute(
"CREATE TABLE IF NOT EXISTS records ("
" id INTEGER PRIMARY KEY, serial TEXT, ts REAL)")
# 写入(参数化,防注入)
ctx.store.execute("INSERT INTO records(serial, ts) VALUES(?, ?)",
("ABC123", time.time()))
# 查询
rows = ctx.store.query(
"SELECT * FROM records WHERE serial=? ORDER BY ts DESC LIMIT 10",
("ABC123",))三条规矩:只能访问本模块自己的库;必须用参数化查询(? 占位符);向 query() 传写语句、或向 execute() 传查询语句,都会被拒绝。
批量写优化
每次写入都会落盘并加密,单条循环写明显变慢。
# 慢:N 次落盘
for k, v in data.items():
ctx.store.set(k, v)
# 快:1 次落盘
ctx.store.set_many(data)
# 快:混合 KV 与 SQL,也只落盘一次
with ctx.store.transaction():
ctx.store.set("last_sync", time.time())
ctx.store.execute("DELETE FROM records WHERE ts < ?", (cutoff,))事务块内抛异常会自动回滚。
清空与关闭
ctx.store.wipe() # 清空本模块全部数据(KV + 自建表),不可恢复
ctx.store.close() # 正常无需手动调用,宿主在卸载/退出时处理版本升级与数据兼容
升级模块不会自动清除历史数据。数据结构变了要自己兼容:
def on_enable(self):
ver = self.ctx.store.get("schema_version", 0)
if ver < 2:
# 先看一眼现有表结构再决定怎么改(PRAGMA 走 query 只读接口)
cols = [r["name"] for r in self.ctx.store.query("PRAGMA table_info(records)")]
if "note" not in cols:
self.ctx.store.execute("ALTER TABLE records ADD COLUMN note TEXT")
self.ctx.store.set("schema_version", 2)不要绕过 ctx.store
直接 open 写文件、自己连 sqlite,都会失去加密和隔离,审核也过不去。真要读写宿主管理之外的文件,声明 fs 权限并想清楚理由。