Language clients
Implement another Workhorse runtime against the shared SQL protocol and conformance fixtures.
Every language client translates application calls into one shared SQL protocol. PostgreSQL owns durable behavior, so clients preserve its decisions instead of recreating them in memory.
Start with the shared contract
The fixtures under protocol/ define canonical requests, results, transitions, and structured
errors. A client runs every applicable fixture against its test database.
The manifest lists every request, schedule, and interpreter fixture by identifier. Each client compares that list with the fixture files and its executed cases, so a new or skipped case fails the suite. The interpreter fixture also keeps matcher types, captured values, normalization, and error details consistent while each language retains its own implementation.
Those fixtures matter when a producer and worker use different languages, or when deployments run different package versions. They keep both sides on one durable contract without requiring either client to understand the other's implementation.
Before a mutation, the client reads workhorse.schema_version and the fixture manifest. If the
installed schema or protocol is incompatible, the client stops before it changes durable state.
PostgreSQL decides whether enqueue accepts or coalesces work. It also owns fences, retries, cancellation, checkpoints, timers, dependencies, child joins, signals, and human decisions.
Keep application transactions in charge
An enqueue client wraps a connection or transaction that the application owns. The client sends SQL through that resource, but it does not commit, roll back, close, or retain it. This lets the application commit its data and the job together:
with psycopg.connect(database_url) as connection:
with connection.transaction():
connection.execute("INSERT INTO orders (id) VALUES (%s)", (order_id,))
Queue(connection).enqueue("order.accepted", {"orderId": order_id})A worker must commit each claim before handler code starts. A runtime may own a pool or a dedicated connection according to its language conventions. Notification listening needs a connection that can retain session state. The worker runs application effects outside the claim transaction, then settles through the fence PostgreSQL issued.
The runtime owns handler registration, bounded concurrency, notifications or polling, heartbeats, local cancellation signals, telemetry, and graceful drain. A batch runtime may group claimed jobs, but each member keeps its own fence and lifecycle.
Translate names, not behavior
The public names follow each language's conventions. These names reach the same protocol boundary:
| Concern | TypeScript | Python | Go |
|---|---|---|---|
| Application client | Queue | Queue / AsyncQueue | Queue |
| Operator client | Admin | Admin / AsyncAdmin | Admin |
| Single enqueue | Queue.enqueue | Queue.enqueue | Queue.Enqueue |
| Batch enqueue | Queue.enqueueMany | Queue.enqueue_many | Queue.EnqueueMany |
| Schedule sync | Queue.syncSchedules | Queue.sync_schedules | Queue.SyncSchedules |
| Cancellation | Queue.cancel | Queue.cancel | Queue.Cancel |
| Worker runtime | Worker | Worker / AsyncWorker | Worker |
| Handler registration | Worker.handle | Worker.handle | Worker.Handle |
| Batch registration | Worker.handleBatch | Worker.handle_batch | Worker.HandleBatch |
| Run loop | Worker.run | Worker.run | Worker.Run |
| One dispatch pass | Worker.runOnce | Worker.run_once | Worker.RunOnce |
If a language needs a different shape, it may adapt resource ownership and async control flow. It must still send canonical JSON, preserve SQLSTATE errors, honor fences, and pass the shared fixtures.
Add one capability at a time
Start at the SQL boundary and prove the behavior with the shared fixture. Then expose the public method, translate its result and error names, and run a clean consumer test against the packaged client. A repository import can hide missing files or dependencies that users will discover later.
When a capability is added, update its parity registry entry and explain it once in the guide that owns the concept. This page maps the shared names, while the owning page explains the behavior.
Continue reading
- Transactional enqueue explains why enqueue uses the caller's transaction.
- Payload contracts explains how clients share payload schemas.
- Workers explains what the worker process supplies.
For exact protocol and ownership rules, use the architecture reference.