Acta. apache-2.0 · .net 10 · pg / mssql / sqlite

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.

Nine questions answered for Kafka, RabbitMQ, Amazon SQS, and Acta
QuestionKafkaRabbitMQAmazon SQSActa
Stores work asA log of records, per partitionMessages in queuesMessages in queuesRows in your own database: the job, its live state, its steps, and its events
Replays old workYes, a consumer rewinds to an offsetStreams onlyNoNo offset replay. An operator restarts a finished job, and every past attempt stays queryable in SQL
Keeps itUntil the topic's retentionUntil acknowledgedUp to 14 daysAn unfinished job is never dropped. A finished one is kept for its retention window, 90 days by default, set per job
OrderPer partitionPer queue, with one consumerFIFO per message groupPer 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
ConsumersPullPushedLong pollPull. 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 rulesTopics and partition keysExchanges and bindingsNoBy job: a namespace and a job name. A worker runs the namespaces it registers
Dead lettersBuild your ownDead-letter exchangeRedrive policyBuilt 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 managedSelf-run, or Amazon MSKSelf-run, or Amazon MQYesNothing to run: a NuGet package inside your application, on the database you already run and back up
Pick it whenMany readers replay one streamTasks need routing to different workersYou are on AWS and want no serversYour 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.

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