honker: queues in the same SQLite file, a version counter polled every millisecond, and two lint suppressions with the reasoning written down
SQLite extension + bindings for Postgres NOTIFY/LISTEN semantics with durable queues, streams, pub/sub, and scheduler
At a glance
- What is it?
- A loadable SQLite extension plus twelve language bindings that put durable queues, streams and pub/sub in the same database file as your data, so enqueueing work and writing the row that created it commit together. It has no broker, wakes workers by polling a cheap version counter, overtriggers on purpose, and calls itself alpha software while naming its competitors and telling Postgres users to stay on Postgres.
- Who is it for?
- honker fits a shipped application whose primary datastore is a single SQLite file and whose background work can be written in the same transaction as the business row. It does not fit a multi-machine deployment or a team with a Postgres queue already.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 4 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Star history
- russellromney/honker3,037 ★ · +24
| Repository | 2026-08-24 | 2026-10-22 |
|---|---|---|
| russellromney/honker | 3k | 3k |
24 Stars in the last 59 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
The queue is rows in the same file as your data
The whole premise is one sentence: if SQLite is your primary datastore, the queue should live in the same file. The implementation of that is deliberately boring. The queue is a table with a partial index, the pub/sub is an insert into a live table, and the streams are rows with per consumer offsets. None of it is a separate service, and the README is explicit that there is no daemon and no broker, which is the thing it is replacing. The consequence is the property the whole design exists for: an enqueue and the business insert can sit in one transaction, so a commit lands both rows and a rollback drops both. The extension is usable from any SQLite client, and the README prints its SQL surface:
SELECT honker_bootstrap();
INSERT INTO _honker_live (queue, payload) VALUES ('emails', '{"to":"alice"}');
SELECT honker_claim_batch('emails', 'worker-1', 32, 300); -- JSON array
SELECT honker_ack_batch('[1,2,3]', 'worker-1'); -- DELETEs; returns count
SELECT honker_lock_acquire('backup', 'me', 60); -- 1 = got it, 0 = heldThere is no dual write window and no second backup story, which is the argument the README makes against the usual answer of adding a separate cache and task framework alongside your database.
Wake-ups come from a version counter polled once a millisecond
SQLite has no server side push channel, so the notification mechanism is a shared watcher, and the details are given rather than glossed. The stable backend reads a pragma that reports the database file's data version every millisecond, and when that counter changes, listeners re-read indexed state. The claimed cost is a single digit microsecond read, and the claimed delivery is single digit milliseconds across processes. The interval is a dial, not a constant: the README says to raise it when idle processor time matters more than the lowest possible wake latency. Optional source builds add two faster backends, one using kernel file events and one reading shared memory, and the bindings document says which language bindings expose those options and what the continuous integration actually proves. That last phrase is worth noting, because it distinguishes a backend that is documented from one that is tested.
Overtriggering is deliberate, and the README explains the tradeoff
One paragraph is the most useful thing in the documentation and it explains a design choice that would otherwise look like a bug. If you point honker at your application's existing database file, it wakes workers on every commit to that file, including the many commits that have nothing to do with any queue. Most wakes find no work. The README says that overtriggering is on purpose, because one indexed select is cheap while a missed wake is a correctness bug. Underneath that are four stated guarantees: wake on committed updates, ignore rolled back work, re-read state after every wake, and use file backed databases rather than an in memory one. That last guarantee is a real constraint on adopters and on test suites, since the usual in memory database is exactly what will not work.
The non-goals are listed, including distributed locking across machines
Many projects imply their limits. This one states them. Deliberately not included are workflow directed acyclic graphs, task chains, groups and chords, multi writer replication, and distributed locking across machines. The framing is single machine and file backed, and the sentence about the database's locking model is blunt: it is designed for one host writing one database file, and two servers writing the same file over a network filesystem is not a deployment strategy. The prior art section reinforces the boundary by naming the Postgres equivalents as the things being chased, calling a couple of them gold standards, pointing at an existing SQLite backed task queue as excellent, and then telling readers who already run Postgres to use the Postgres tools because they are excellent. Very few READMEs tell a reader to keep their current stack.
Two release lines published a minute apart
The release history has three visible entries and two naming schemes. There is a version zero point five tag and, one minute later on the same day, an extension prefixed version zero point five tag with the SQLite extension in the name. The next release, zero point six, describes itself by content rather than by number: listeners for two more languages and a checkpoint fix in the Node binding. So the project ships the core engine and the loadable extension on separate version lines, and the description of a release tells you what changed rather than what broke. The root manifest makes the packaging arrangement explicit by declaring itself a development workspace that is not a publishable package, with an empty dependency list, and pointing readers at a packages directory. Anything a user installs therefore comes from that directory or from a language registry, not from this repository's root.
Two linter suppressions, each with the reason written next to it
The Rust workspace configuration carries a shared lint policy with a comment explaining that it exists so a local run and the continuous integration job agree, and that the job turns warnings into failures. Two warnings are then allowed, and the reasoning for both is written in full. One is an argument count lint, allowed because collapsing eight arguments into a parameter struct would be a breaking change to an already published interface, and the comment says that belongs in a deliberate future version rather than in a lint sweep. The other is a type complexity lint, allowed because the two sites in question are row tuples read straight out of a query call, and naming them would add a type alias that has to be hand synchronised with the select list. That is what a written lint policy looks like: not a list of suppressions but a record of what was considered and why the answer was no.
The Node package is published under a different scope than the repository
The bindings table lists twelve ecosystems, and the install line for one of them does not match the repository owner. The Ruby gem, the NuGet package, the Rust crates, the Elixir package and the Python distribution are all published under the project's own name, while the Node package is installed from a scope belonging to a different account. That may be a deliberate choice about who maintains that binding, and the repository does not say. The bindings are also split across two arrangements at once, which the Rust workspace configuration spells out: every language binding lives in its own submodule repository and opts out of this workspace with its own table, while a set of them is maintained in the repository's own packages directory and excluded from the Rust workspace instead. The core crates are pulled by each binding with a path and a version together, so the path resolves when the binding is submoduled and the version takes over when it is installed from a registry on its own.
The default test run skips the slow and the Linux-only tests
The workspace manifest configures the test runner for the whole repository and is explicit about what a default run leaves out. Two markers are defined: one for long running tests involving real time waits and soak runs that the comment says should run nightly, and one for tests needing Linux specific infrastructure such as temporary filesystem mounts. The default add options combine parallel execution with both markers excluded, and three comment lines spell out how to run the excluded sets individually or everything. That is careful work, and it has a consequence for anyone reading a passing build: the default run is not the full matrix. The build file makes the same point in a different place, describing its local continuous integration targets as Linux only and explicitly not full parity, with one of them noting there is still no Windows.
Editorial conclusion
honker fits a shipped application whose primary datastore is a single SQLite file and whose background work can be written in the same transaction as the business row. It does not fit a multi-machine deployment or a team with a Postgres queue already. Four things to know. The design is single machine and file backed, and the README says two servers writing one file over a network filesystem is not a deployment strategy. Wake-ups come from polling a version counter on an interval you choose, so latency and idle processor time are a dial rather than a property. Memory backed databases are explicitly not supported, which rules out most test setups. And the extension's tables are reachable from plain SQL, so any code with write access to the file can also corrupt queue state.
Frequently asked questions
What is honker?
A SQLite loadable extension plus language bindings that add Postgres style notify and listen semantics to SQLite, with durable queues, event streams and time triggered scheduling. Any SQLite client that can load an extension gets the same functions, so a worker in one language can claim work enqueued by another.
Does honker need a broker or a daemon?
No. The README says it avoids client polling and any daemon or broker: the queue is rows in a table with a partial index in the same file as your data, and wake-ups come from a shared watcher that reads a cheap version counter once per millisecond by default.
How durable are honker queues?
At least once, with retries, delayed jobs, priority, visibility timeouts, dead-letter rows and task result storage. Enqueue, stream publish and notify are inserts inside your transaction, so a commit lands the business row and the work together and a rollback drops both.
What does honker deliberately not support?
Workflow directed acyclic graphs, task chains, groups and chords, multi writer replication, and distributed locking across machines. It is single machine and file backed, and the README states that two servers writing the same database file over a network filesystem is not a deployment strategy.
Which languages does honker support?
Thin bindings for Python, Node.js, Rust, Go, Ruby, Bun, Elixir, C++, .NET with C#, Java on the JVM and Kotlin, plus the loadable extension for any SQLite client from version 3.9 onward. A parity table in the bindings document covers what each one supports, and the Node package is published under a different account than the repository.
Is honker ready for production?
The README calls it alpha software: better than experimental, but not of beta quality yet. It also names its prior art, calls two Postgres queues the standards it is chasing, points at an existing SQLite backed queue as excellent, and tells readers who already run Postgres to use the Postgres tools.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/russellromney-honker)Star history chart for your README
[](https://hysenlabs.com/projects/russellromney-honker)