queued

Fast zero-configuration single-binary simple queue service.

Quick start

queued requires persistent storage, and it's preferred to provide a block device directly (e.g. /dev/my_block_device), to bypass the file system. Alternatively, a standard file can be used too (e.g. /var/lib/queued/data). In either case, the entire device/file will be used.

Install

```

Ensure you have Rust installed.

cargo install queued

--format WILL DESTROY EXISTING CONTENTS.

queued --device /dev/myblockdevice --format ```

Run

queued --device /dev/my_block_device

Call

```jsonc // 🌐 POST localhost:3333/push { "messages": [ { "contents": "Hello, world!", "visibilitytimeoutsecs": 0 } ] } // ✅ 200 OK { "id": 190234 }

// 🌐 POST localhost:3333/poll { "visibilitytimeoutsecs": 30 } // ✅ 200 OK { "message": { "contents": "Hello, world!", "created": "2023-01-03T12:00:00Z", "id": 190234, "pollcount": 1, "polltag": "f914659685fcea9d60" } }

// 🌐 POST localhost:3333/update { "id": 190234, "polltag": "f914659685fcea9d60", "visibilitytimeout_secs": 15 } // ✅ 200 OK {}

// 🌐 POST localhost:3333/delete { "id": 190234, "poll_tag": "f914659685fcea9d60" } // ✅ 200 OK {} ```

Performance

With a single Intel Alder Lake CPU core and NVMe SSD, queued manages around 250,000 operations (push, poll, or delete) per second with 4,096 concurrent clients and a batch size of 64. There is minimal memory usage; only a pointer to each message's storage data is stored in memory.

Safety

At the API layer, only a successful response (i.e. 2xx) means that the request has been successfully persisted to disk. Assume any interrupted or failed requests did not safely get stored, and retry as appropriate. Changes are immediately visible to all other callers.

It's recommended to use error-detecting-and-correcting durable storage when running in production, like any other stateful workload.

Performing backups can be done by stopping the process and taking a copy of the contents of the file/device. Using compression can reduce bandwidth (when transferring) and storage usage.

Management

POST /suspend can suspend specific API endpoints, useful for temporary debugging or emergency intervention without stopping the server. It takes a request body like:

json { "delete": true, "poll": true, "push": false }

Set a property to true to disable that endpoint, and false to re-enable it. Disabled endpoints will return 503 Service Unavailable. Use GET /suspend to get the currently suspended endpoints.

POST /throttle will configure poll throttling, useful for flow control and rate limiting. It takes a request body like:

json { "throttle": { "max_polls_per_time_window": 100, "time_window_sec": 60 } }

This will rate limit poll requests to 100 every 60 seconds. No other endpoint is throttled. Throttled requests will return 429 Too Many Requests. Use GET /throttle to get the current throttle setting. To disable throttling:

json { "throttle": null }

GET /healthz returns the current build version.

GET /metrics returns metrics in the Prometheus or JSON (Accept: application/json) format:

```

HELP queuedemptypoll Total number of poll requests that failed due to no message being available.

TYPE queuedemptypoll counter

queuedemptypoll 0 1678525380549

HELP queued_invisible Amount of invisible messages currently in the queue. They may have been created, polled, or updated.

TYPE queued_invisible gauge

queued_invisible 0 1678525380549

HELP queuediosyncbackgroundloops Total number of delayed sync background loop iterations.

TYPE queuediosyncbackgroundloops counter

queuediosyncbackgroundloops 19601 1678525380549

HELP queuediosync Total number of fsync and fdatasync syscalls.

TYPE queuediosync counter

queuediosync 0 1678525380549

HELP queuediosync_delayed Total number of requested syncs that were delayed until a later time.

TYPE queuediosync_delayed counter

queuediosync_delayed 0 1678525380549

HELP queuediosynclongestdelay_us Total number of microseconds spent waiting for a sync by one or more delayed syncs.

TYPE queuediosynclongestdelay_us counter

queuediosynclongestdelay_us 0 1678525380549

HELP queuediosyncshortestdelay_us Total number of microseconds spent waiting after a final delayed sync before the actual sync.

TYPE queuediosyncshortestdelay_us counter

queuediosyncshortestdelay_us 0 1678525380549

HELP queuediosync_us Total number of microseconds spent in fsync and fdatasync syscalls.

TYPE queuediosync_us counter

queuediosync_us 0 1678525380549

HELP queuediowrite_bytes Total number of bytes written.

TYPE queuediowrite_bytes counter

queuediowrite_bytes 0 1678525380549

HELP queuediowrite Total number of write syscalls.

TYPE queuediowrite counter

queuediowrite 0 1678525380549

HELP queuediowrite_us Total number of microseconds spent in write syscalls.

TYPE queuediowrite_us counter

queuediowrite_us 0 1678525380549

HELP queuedmissingdelete Total number of delete requests that failed due to the requested message not being found.

TYPE queuedmissingdelete counter

queuedmissingdelete 0 1678525380549

HELP queuedmissingupdate Total number of update requests that failed due to the requested message not being found.

TYPE queuedmissingupdate counter

queuedmissingupdate 0 1678525380549

HELP queuedsuccessfuldelete Total number of delete requests that did delete a message successfully.

TYPE queuedsuccessfuldelete counter

queuedsuccessfuldelete 0 1678525380549

HELP queuedsuccessfulpoll Total number of poll requests that did poll a message successfully.

TYPE queuedsuccessfulpoll counter

queuedsuccessfulpoll 0 1678525380549

HELP queuedsuccessfulpush Total number of push requests that did push a message successfully.

TYPE queuedsuccessfulpush counter

queuedsuccessfulpush 0 1678525380549

HELP queuedsuccessfulupdate Total number of update requests that did update a message successfully.

TYPE queuedsuccessfulupdate counter

queuedsuccessfulupdate 0 1678525380549

HELP queuedsuspendeddelete Total number of delete requests while the endpoint was suspended.

TYPE queuedsuspendeddelete counter

queuedsuspendeddelete 0 1678525380549

HELP queuedsuspendedpoll Total number of poll requests while the endpoint was suspended.

TYPE queuedsuspendedpoll counter

queuedsuspendedpoll 0 1678525380549

HELP queuedsuspendedpush Total number of push requests while the endpoint was suspended.

TYPE queuedsuspendedpush counter

queuedsuspendedpush 0 1678525380549

HELP queuedsuspendedupdate Total number of update requests while the endpoint was suspended.

TYPE queuedsuspendedupdate counter

queuedsuspendedupdate 0 1678525380549

HELP queuedthrottledpoll Total number of poll requests that were throttled.

TYPE queuedthrottledpoll counter

queuedthrottledpoll 0 1678525380549

HELP queued_vacant How many more messages that can currently be pushed into the queue.

TYPE queued_vacant gauge

queued_vacant 0 1678525380549

HELP queued_visible Amount of visible messages currently in the queue, which can be polled. This may be delayed by a few seconds.

TYPE queued_visible gauge

queued_visible 4000000 1678525380549 ```

Important details

Development

queued is a standard Rust project, and does not require any special build tools or system libraries.

As the design and functionality is quite simple, I/O tends to become the bottleneck at scale (and at smaller throughputs, the performance is more than enough). This is important to know when profiling and optimising.

Clients in example-client can help with running synthetic workloads for stress testing, performance tuning, and profiling.

As I/O becomes the main attention for optimisation, keep in mind: - write syscall data is immediately visible to all read syscalls in all threads and processes. - write syscalls can be reordered, unless fdatasync/fsync is used, which acts as both a barrier and cache-flusher. This means that a fast sequence of write (1 create) -> read (2 inspect) -> write (3 update) can actually cause 1 to clobber 3. Ideally there would be two different APIs for creating a barrier and flushing the cache.