- Update architecture docs to reflect pivot from @libsql/client to Honker - Fold @alkdev/drizzlebox Phase 0 into src/sqlite/utils/ (ADR-046) - Add HonkerEventTarget adapter for pubsub TypedEventTarget (ADR-047) - Replace hand-written CRUD with OperationSpec generation (ADR-048) - Resolved OQ-26: Honker replaces Redis for single-node pub/sub (POC validated) - Updated OQ-17, OQ-18, OQ-19 for OperationSpec repository surface - Added OQ-30 (composite event target), OQ-31 (consumer naming), OQ-32 (Drizzle Kit) - POC results: adapter buildable, same-process pub/sub works, transactional outbox semantics confirmed, concurrent listeners/streams work - Research doc at docs/research/pivot-honker-sqlite-adapter.md
2.6 KiB
2.6 KiB
status, date, resolves
| status | date | resolves |
|---|---|---|
| accepted | 2026-06-01 | OQ-26 |
ADR-047: HonkerEventTarget Adapter for pubsub
Context
@alkdev/pubsub defines a TypedEventTarget interface that all transport
adapters implement: in-process EventTarget, Redis, WebSocket client/server,
and Worker. This provides transport-agnostic pub/sub — consumers call
createPubSub({ eventTarget }) without knowing the underlying transport.
Honker provides SQLite with built-in pub/sub primitives:
db.notify(channel, payload)/db.listen(channel)— ephemeral, fire-and-forgetdb.stream(name).publish(payload)/db.stream(name).subscribe(consumer)— durable, offset-tracked
POC 2-4 (2026-06-01) validated:
- Same-process notify→listen works. ~17ms median latency.
- Multiple concurrent listeners on different channels work.
tx.notify()only fires ontx.commit(). Rollback suppresses notification.queue.enqueueTx(tx, payload)only visible after commit. Rollback suppresses.- Stream publish/subscribe works with consumer offset tracking.
These results confirm Honker can back the pubsub TypedEventTarget
interface for single-node deployments.
Decision
Implement HonkerEventTarget in src/sqlite/event-target.ts. It adapts
@alkdev/pubsub's TypedEventTarget to Honker primitives with two modes:
- Ephemeral mode:
addEventListener→db.listen(),dispatchEvent→db.notify(). Fire-and-forget semantics, no delivery guarantee. - Durable mode:
addEventListener→db.stream().subscribe(),dispatchEvent→db.stream().publish(). Per-consumer offset tracking, crash recovery replays from last saved offset.
@alkdev/pubsub is a peer dependency (needed only when using
HonkerEventTarget). The graphs/ module remains zero-dep.
Consequences
- Single-node hub can use Honker instead of Redis — no separate Redis deployment needed for pub/sub.
- Transactional outbox semantics —
dispatchEventinside a Drizzle transaction (viatx.notify()orstream.publishTx()) commits atomically with data writes. No dual-write problem. - Hub-spoke symmetry — both hub and spoke use the same
createPubSub()call. Different event target instances determine routing. - Multi-node still needs Redis or WebSocket — Honker events don't cross
process boundaries. For multi-node, the
WebSocketServerEventTarget(hub) andWebSocketClientEventTarget(spoke) handle cross-process routing. - Latency trade-off — ~17ms Honker round-trip vs sub-ms in-process. For
hot-path call protocol, pair with in-process
EventTarget. Design of a composite event target is an open question (OQ-30).