如果你的应用已经把 SQLite 当作主存储,那么任务队列是否也应该和数据“共存”在同一个文件里?
honker 为 SQLite 增加了类似 PostgreSQL 的 NOTIFY/LISTEN 语义,同时提供持久化的 pub/sub、任务队列和事件流功能,而且不需要客户端轮询,也不需要独立的守护进程或消息中间件(broker)。在 M 系列芯片的笔记本上,跨进程唤醒延迟 p50 约为 0.7 毫秒。
在最基础的形式下,它只是一个普通的 SQLite 可加载扩展,因此任何支持 SQLite 已经在真实生产环境中承担关键业务,例如 Bluesky 的 PDS、Fly 的 LiteFS、Turso。一旦真实业务流进入基于 SQLite 的应用,就必然需要队列系统。传统方案通常是“加 Redis + Celery”。这种方式是可行的,但会引入第二套数据存储系统及其备份方案,还会带来业务表与队列之间的双写问题,以及运行消息中间件的运维成本。 honker 的思路是:如果 SQLite 是主存储,那么队列也应该存在于同一个文件中。 这意味着 Honker = SQLite + 内建队列系统(无外部依赖) 在一次原子事务中,同时完成业务写入与任务入队,然后进行消费。所有操作都在同一个 或者使用类似 Huey 的装饰器方式: honker 每隔 1 毫秒轮询一次 SQLite 的 后台线程会把这个 tick 分发给所有订阅者,然后每个订阅者执行: 并返回新的数据行。对于每个数据库而言,无论订阅者数量多少,都只需要一个轮询线程。 在空闲状态下的成本是:每个数据库每毫秒执行一次轻量级 SELECT。但不会带来 page-cache 压力,不会造成写锁竞争,也不依赖操作系统的文件监听机制。由于“唤醒信号”是一次共享轮询,而不是为每个监听器单独执行查询,所以监听器数量可以自由扩展。 队列、数据流以及 pub/sub 原语,本质上都是扩展所管理表中的 INSERT 操作。 在你的业务事务中调用: 意味着这个 job 行与前面的: 处于同一个 ACID 事务中。如果事务回滚,job 也会一并被撤销。 Huey[2] 是基于 SQLite 的 Python 任务队列,honker 主要参考的就是它的思路。 pg-boss[3] 和 Oban[4] 则是 Postgres 生态里比较成熟的队列方案。 如果你本来就已经在用 Postgres,那直接用这些就可以了。
SELECT load_extension('honker_ext') 的语言,都可以在同一个数据库文件上使用相同的队列、流和通知机制。Python、Node、Rust、Go、Ruby、Bun 和 Elixir 的绑定共享同一种磁盘数据格式。INSERT INTO orders 和 queue.enqueue(...) 可以在同一个事务中提交;如果事务回滚,两者都会一起撤销。队列本质上只是由表和部分索引组成的数据行。一句话理解:
一个例子
.db 文件中完成,采用统一的磁盘格式,并可在七种语言中使用。import honker
db = honker.open("app.db")
q = db.queue("emails")
# 在同一个事务中:业务写入 + 入队
with db.transaction() as tx:
tx.execute("INSERT INTO orders (id, total) VALUES (?, ?)", [42, 99])
q.enqueue({"to": "wujm_xa@qq.com", "order_id": 42}, tx=tx)
# worker 在数据库任何提交发生时被唤醒,无需轮询
async for job in q.claim("worker-1"):
await send_email(job.payload)
job.ack()@q.task(retries=3, timeout_s=30)
def send_email(to, subject):
...
return {"sent_at": time.time()}
r = send_email("wujm_xa@qq.com", "Hi") # 入队,返回 TaskResult
print(r.get(timeout=10)) # 阻塞直到 worker 执行完成工作原理
PRAGMA data_version。这是一个单调递增计数器,SQLite 会在每次来自任意连接、任意日志模式或任意进程的提交发生时对其加一——读取它大约只需要 3 微秒,却可以作为一个非常精确的“唤醒信号”。SELECT ... WHERE id > last_seen
queue.enqueue(payload, tx=tx)
INSERT INTO orders
相关工作
pg_notify[1] 能做跨进程的快速触发,但它不负责重试,也没有任务可见性这类能力。引用链接
[1] https://www.postgresql.org/docs/current/sql-notify.html[2] https://github.com/coleifer/huey[3] https://github.com/timgit/pg-boss[4] https://github.com/sorentwo/oban
本文链接:https://kinber.cn/post/6562.html 转载需授权!
推荐本站淘宝优惠价购买喜欢的宝贝:

支付宝微信扫一扫,打赏作者吧~
