Show HN: Honker – SQLiteのためのPostgres NOTIFY/LISTENセマンティクス
概要
honkerは、SQLiteにPostgresのNOTIFY/LISTEN機能を追加する拡張機能。
耐久性のあるpub/sub・タスクキュー・イベントストリームを提供し、ポーリングやブローカー不要。
Rust製SQLite拡張として実装され、**Python/Node.js/Ruby/Go/Elixir/C++**など多言語対応。
WALファイルのイベント通知で高速なプッシュセマンティクスを実現。
RedisやCelery不要で、業務処理とキュー操作を同一トランザクションで管理可能。
honker概要と特徴
- SQLite用拡張機能で、PostgresのNOTIFY/LISTENに近いpub/sub・キュー機能を実装
- Rust crate(honker, honker-core, honker-extension)として提供
- **Python(honker)/Node.js(@russellthehippo/honker-node)/Bun/Ruby/Go/Elixir/C++**向けラッパー
- on-diskレイアウトはRustで一元管理、各言語バインディングは薄いラッパー
- WALファイルの変更検知でイベント発火、シングルミリ秒台の通知遅延を実現
- 耐久性のあるpub/sub、タスクキュー、イベントストリームを1つのSQLiteファイルで管理
- ポーリング不要・デーモンや外部ブローカー不要、シンプルな運用
- トランザクション内で業務処理とキュー投入が同時コミット、ロールバックも一括
- 各種リトライ、優先度、遅延実行、デッドレター、レートリミット等もサポート
- APIは実験的段階、将来的に変更の可能性
honker導入の背景とメリット
- SQLiteがメインDBの場合、pub/subやタスクキューも同じファイルで完結
- RedisやCelery等の外部システム不要、バックアップや運用負荷を削減
- ordersテーブルへのINSERTとqueue.enqueueを同一トランザクションで管理
- 過去事例:pg_notify(Postgres)、Huey(Python)、pg-boss/Oban(Postgres)等を参考
- Postgres利用中ならpg_notifyやOban推奨、SQLiteユーザー向け
主要機能一覧
- 1つの.dbファイル内でプロセス間通知(notify/listen)
- リトライ・優先度・遅延・デッドレター付きワークキュー
- 業務処理とキュー投入のアトミックコミット
- ミリ秒単位の高速リアクション、ポーリング不要
- ハンドラータイムアウト、エクスポネンシャルバックオフによるリトライ
- 遅延ジョブ、タスク有効期限、ネームドロック、レートリミット
- crontab式定期タスク(リーダー選出スケジューラ付き)
- タスク結果の保存・取得(IDで管理、ワーカーが結果永続化)
- 耐久性のあるストリーム(各コンシューマごとにオフセット管理)
- SQLite拡張としてどのクライアントからも同一テーブル参照可能
- Python, Node.js, Rust, Go, Ruby, Bun, Elixir向けバインディング
クイックスタート例(Python)
-
pip install honkerでインストール
-
**db = honker.open("app.db")**でDBオープン、**emails = db.queue("emails")**でキュー生成
-
トランザクション内で業務処理とキュー投入をアトミックに実行
with db.transaction() as tx: tx.execute("INSERT INTO orders (user_id) VALUES (?)", [42]) emails.enqueue({"to": "alice@example.com"}, tx=tx) -
ワーカー側で非同期イテレータでジョブを取得・処理
async for job in emails.claim("worker-1"): try: send(job.payload) job.ack() except Exception as e: job.retry(delay_s=60, error=str(e)) -
claim()はWALコミットで即時起床、WAL監視不可時のみ5秒ポーリング
-
デフォルトvisibilityは300秒、バッチ処理もclaim_batchで対応
タスクデコレータ(Python)
-
関数を@emails.taskで装飾し、直接enqueueせずジョブ化可能
@emails.task(retries=3, timeout_s=30) def send_email(to: str, subject: str) -> dict: ... return {"sent_at": time.time()} -
呼び出し側:r = send_email("alice@example.com", "Hi")、r.get(timeout=10)で結果取得
-
ワーカーはpython -m honker worker myapp.tasks:db --queue=emails --concurrency=4で起動
-
*定期タスクは@emails.periodic_task(crontab("0 3 * * "))で実装
ストリーム・通知(Python)
- 耐久性pub/sub:db.stream("user-events")でストリーム取得、publish/subscribeで利用
- 各コンシューマは自身のオフセットを_honker_stream_consumersで管理
- subscribeは履歴リプレイ→WAL通知でライブ配信に移行、オフセット自動保存可
- ephemeral pub/sub:db.listen("orders")でリスナ起動、tx.notify("orders", {...})で通知
- 耐久性リプレイ不要ならlisten/notify、必要ならstream/publish推奨
- 通知テーブルは自動クリーンアップされないため、db.prune_notificationsで管理
Node.js例
- const { open } = require('@russellthehippo/honker-node')でDBオープン
- トランザクションで業務処理+notifyをアトミックに実行
- for await (const n of db.listen('orders'))で通知受信
SQLite拡張機能のSQL例
- .load ./libhonker_extで拡張読込
- SELECT honker_bootstrap()で初期化
- INSERT INTO _honker_liveでキュー投入
- SELECT honker_claim_batchでジョブ取得、SELECT honker_ack_batchでACK
- 各種ロック、レートリミット、スケジューラ、ストリーム、結果保存APIも提供
設計思想とアーキテクチャ
- Python/Node/Rust/Go/Ruby/Bun/Elixir向けバインディングを同梱
- Huey(Python SQLiteキュー)を参考に設計
- Postgresならpg_notify+pg-boss/Obanが相当、honkerはSQLite向け
- 3つのプリミティブ:ephemeral pub/sub(notify)、durable pub/sub(stream)、at-least-once queue(queue)
- 全てトランザクション内INSERTでアトミック
- NOTIFY/LISTENセマンティクスをポーリング無しで実現、WALコミットごとに通知
- WALモード必須(journal_mode = WAL)
- WALファイルのstat(2)で変更検知、クロスプラットフォームで1ms精度
- .db + .db-walのみで完結、運用・スナップショット容易
- サーバープッシュ不可のため、ファイル変更→SELECTで通知
- トランザクションは安価、ジョブ・イベント・通知は全てテーブルの行として管理
注意点・非対応事項
- WALモード必須、DELETE/TRUNCATEモード非対応
- 複数ライター複製、DAG型ワークフロー等は非対応
- バックアップ先や共有ファイルシステム等、WALモード非推奨環境では利用不可
- API・仕様は今後変更の可能性あり
このように、honkerはSQLite単体で高機能なpub/sub・タスクキュー・イベントストリームを実現するための軽量・高性能拡張です。外部システム無しで、業務処理とキュー処理のアトミック性を重視するプロジェクトに最適です。