Solo
£29 / month
60 days trial — no card required
- 1 spool
- 250 req/s
- 7 days retention
Per spool:
- 1,000 queues
- 100,000 stored messages
- 2 GiB stored data
- 64 KiB per message
- 1 replica
Spooler is a fully managed message queue.
Messages go in, messages come out. Leases, retries, and recovery are already in between.
import "github.com/spoolersh/spooler-go" // A client for the public API. c := spooler.Client{ APIKey: "...", } // Every account has a spool named default; queues live there. // Create one once: it comes with the server's default settings. err := c.CreateQueue(ctx, spooler.CreateQueueRequest{ Spool: "default", Queue: "orders", }) if errors.Is(err, spooler.ErrQueueExists) { err = nil } if err != nil { // Handle the error. }
import "github.com/spoolersh/spooler-go" // A client for the public API. c := spooler.Client{ APIKey: "...", } // The body is the message: bytes in, bytes out, no envelope. // Send returns once it is stored; the id comes back for logs. res, err := c.Send(ctx, spooler.SendRequest{ Spool: "default", Queue: "orders", Data: []byte("hello, spooler!"), }) if err != nil { // Handle the error. } fmt.Println(res.ID) // 1-1
import "github.com/spoolersh/spooler-go" // A client for the public API. c := spooler.Client{ APIKey: "...", } // A receive does not delete the message: it leases it, and no other // receiver gets it while the lease is live; Ack presents it back. msg, ok, err := c.Recv(ctx, spooler.RecvRequest{ Spool: "default", Queue: "orders", }) if err != nil { // Handle the error. } if !ok { // The wait elapsed with nothing to receive. } fmt.Printf("%q", msg.Data) // "hello, spooler!"
import "github.com/spoolersh/spooler-go" // A client for the public API. c := spooler.Client{ APIKey: "...", } // Present the lease back: the message is acknowledged and removed // from the queue. That is the whole loop: send, receive, ack. err := c.Ack(ctx, spooler.AckRequest{ Spool: "default", Lease: msg.Lease, }) if err != nil { // Handle the error. }
import "github.com/spoolersh/spooler-go" // A pipeline stage settles both sides in one call: AckAndSend acks // the delivery and appends the next message, atomically. // A client for the public API. c := spooler.Client{ APIKey: "...", } // The lease names the message acked; Queue names where the new one // lands (create "next" first, as with "orders"). next, err := c.AckAndSend(ctx, spooler.AckAndSendRequest{ Spool: "default", Lease: msg.Lease, Queue: "next", Data: []byte("hello, next stage"), }) if err != nil { // Handle the error. } fmt.Println(next.ID) // 1-3
A send answers only after the message is written and synced to disk, so it survives a crash or restart. An ack is durable the same way: that message will not come back.
A receive leases a message: it stays hidden while you process it and comes back if you never ack. Renew for long jobs. A consumer that dies mid-work costs a redelivery, not a message.
A failing message retries and fails alone while everything behind it flows. Past the queue's retry limit, or the moment your handler fails it deliberately, it rests as failed, intact: a state of the same message, not a place it moves to. List it, peek at it, recover it, or discard it.
A stage that consumes from one queue and produces into the next settles both in one call: the input acked and the output appended together. A retry of a call that already committed appends nothing.
Give a queue a dedup window and each send a key: a repeat with the same key inside the window appends nothing, so a producer retrying after a dropped connection is safe.
A queue can cap how fast it hands out messages: a sustained rate and a burst, enforced on the server and shared by every consumer. Pace a stage to what a downstream API tolerates, without a limiter in each worker.
A spool has a storage cap; at the cap, sends are refused until consumers drain it. A stalled consumer is a full spool, not a surprise invoice.
£29 / month
60 days trial — no card required
Per spool:
£149 / month
45 days trial — card required
Per spool:
£299 / month
30 days trial — card required
Per spool: