brokers · a fit guide
With a message broker or without one, Acta runs your background work.
Kafka, RabbitMQ, and SQS move messages between systems. A broker keeps a message durable until it is acknowledged; the work that message starts, its progress, its retries, its result, and the business change it makes, has to live in a database. Your application already has one. Acta keeps every job, step, retry, and outcome as a row in that PostgreSQL, SQL Server, or SQLite. So the question is not Acta or a broker. Without a broker, you may not need one to run background jobs. With a broker, let it carry the message and give the work to Acta.
The nine questions
The broker comparison table, with an Acta column.
The questions people ask when they pick a broker are the right questions for background work too. The three broker columns are the common summary; check the vendor documentation for your version. The Acta column describes the current engine.
| Question | Kafka | RabbitMQ | Amazon SQS | Acta |
|---|---|---|---|---|
| Stores work as | A log of records, per partition | Messages in queues | Messages in queues | Rows in your own database: the job, its live state, its steps, and its events |
| Replays old work | Yes, a consumer rewinds to an offset | Streams only | No | No offset replay. An operator restarts a finished job, and every past attempt stays queryable in SQL |
| Keeps it | Until the topic's retention | Until acknowledged | Up to 14 days | An unfinished job is never dropped. A finished one is kept for its retention window, 90 days by default, set per job |
| Order | Per partition | Per queue, with one consumer | FIFO per message group | Per lane: one job at a time, in enqueue order, like an SQS FIFO message group. Outside a lane, claims go by priority, then due time; there is no global FIFO |
| Consumers | Pull | Pushed | Long poll | Pull. Workers claim rows from the database; a wakeup, in-process or through Redis across hosts, cuts the wait, and an idle worker still polls once a second |
| Routing rules | Topics and partition keys | Exchanges and bindings | No | By job: a namespace and a job name. A worker runs the namespaces it registers |
| Dead letters | Build your own | Dead-letter exchange | Redrive policy | Built in. A job retries on backoff up to its attempt budget, then stays Failed, raises an alert, and can be inspected and restarted. An outbox row that keeps failing is quarantined |
| Fully managed | Self-run, or Amazon MSK | Self-run, or Amazon MQ | Yes | Nothing to run: a NuGet package inside your application, on the database you already run and back up |
| Pick it when | Many readers replay one stream | Tasks need routing to different workers | You are on AWS and want no servers | Your application's background work must not get lost, and you want its state, history, and controls in your own database |
Row by row: lanes, retention, alerting, the outbox.
Without a broker
You may not need a broker to run background jobs.
Durable work needs a database: that is where the data it changes lives. A queue in front of it is a second store to run, with a gap between the two that you close by hand. Acta puts the job in the same database, and ships what a queue leaves to you.
- The job commits with your data. When the job and the business data share a database, the enqueue joins your
DbTransaction: the order row and its job commit or roll back together, with no dual write. When they do not share one, an outbox table in the business database and an Acta relay carry it across. - Retries and dead letters are already there. Backoff, an attempt budget, a Failed state that raises an alert, and a restart when the fix is in.
- Long work survives a crash. A handler records each step's outcome as a row; after a crash, completed steps return their stored result. A job can sleep for a week or wait for an approval without holding a worker, and fan out into children it waits on.
- Schedules and limits are declared on the job. Cron schedules, deduplication keys, concurrency keys, rate limits, deadlines, and tenants, overridable by an operator without a deploy.
- Every question has an answer in SQL. A dashboard, a CLI, and views such as
jobs_viewandevents_viewshow what ran, what failed, and why, in the database you already back up.
With a broker
Already run a broker? Let it carry the message, and give the work to Acta.
Once a message is acknowledged, the broker is done with it, and the work still needs somewhere durable to live. A consumer takes a message off Kafka, RabbitMQ, or SQS, enqueues an Acta job with a deduplication key from the message id, and acknowledges. A redelivered message finds the existing job instead of starting a second one. From there the work has retries, steps, a timeline, an alert when it fails for good, and an operator who can pause, restart, or explain it. The broker goes back to moving messages between systems.
Division of labor
What stays the broker's job.
Acta is not a broker and does not try to be one. Keep the broker for moving messages:
- Volume that never touches your database
- Durable work is bounded by the database sooner or later, broker or no broker: every unit of work that changes your data commits there. Acta's ceiling is that same commit rate, so it adds no bottleneck of its own. A broker earns its place for traffic that never needs your database, such as event streams, telemetry, and notifications to other systems. The numbers Acta publishes, and how they were measured, are on the home page.
- Fan-out and pub/sub
- One event read by many independent consumers, each at its own pace. In Acta a job has one owner; a parent starting children is orchestration, not publication.
- Replaying a stream
- Rewinding a log to rebuild a projection or feed a new consumer. Acta restarts jobs; it does not replay a log.
- Routing by content
- Exchanges, bindings, header matching, and topic wildcards. Acta routes by job name and namespace.
- Systems in several languages
- A broker connects a Go producer to a Python consumer. Acta's handlers are .NET; other languages can enqueue and read through its HTTP API.
Either way
The work lands in Acta.
If a message must reach many systems, a broker carries it. If a piece of work must run, retry, wait, and be explained afterwards, it needs a database, and Acta records it in the one you already run, with a broker in front of it or without one.
Also: how Acta compares·vs Temporal·order, concurrency, and rate·from Hangfire