Repository navigation
v0.34.0
Request IDs in jobs: a job takes the ID of the request that pushed it, and the queue's log lines of the job say it, so a job that fails, later and on any instance, is found with the request it came from. A request's ID, which middleware.RequestID gives it and middleware.Logger logs, went no further than the request: the auth starter's verify mail, pushed as a user registers, said nothing in its log lines of where it came from, nor in the list of the jobs that failed for good. A queue's Carry lists what a job takes from the context it's pushed from and gives back to the one it runs in, and middleware.CarryRequestID is the request's ID.
The auth starter's queue, in an app made with v0.34.0:
q := queue.New(queue.Config{Store: &jobs{db: db}, Workers: workers, Carry: []queue.Carrier{middleware.CarryRequestID}})registering while the mail server is down:
2026/10/01 21:47:15 INFO request method=POST path=/register status=303 size=0 duration=33.874166ms route="POST /register" request_id=KMXUPJRLQHB2DAYIAQ5YBQZQGJ
2026/10/01 21:47:15 WARN a job failed, and will run again kind=verify-mail job=6 attempt=1 at=2026-10-01T21:47:16.197+07:00 err="mail: dial tcp 127.0.0.1:1: connect: connection refused" request_id=KMXUPJRLQHB2DAYIAQ5YBQZQGJ
and once the job has failed for good:
$ ./blog jobs
1 job failed for good, and is kept for a month:
6 verify-mail, which failed at 2026-10-01 14:47:19 UTC after 10 attempts, pushed by request KMXUPJRLQHB2DAYIAQ5YBQZQGJ
{"user":1,"email":"ann@example.com"}
mail: dial tcp 127.0.0.1:1: connect: connection refused
./blog jobs retry <id> runs one again, and ./blog jobs retry all runs them all.
What's new:
queue.Carrier: what a job takes from the context it's pushed from, and gives back to the context it runs in:Carry(ctx, into)andRestore(ctx, from), over amap[string]string. It's the shape of OpenTelemetry's propagators, so an app's traces go on into its jobs in a few lines, as the guide shows.queue.Config'sCarrylists them.- Carried however it's pushed:
Push,PushAt, a unique kind's and a latest one's, andIn's, in a handler's transaction, each from the context it's given; kept through the job's retries, and when it's run again from the failed; and given back to the handler's context, to aRate's limiter's, and toOnFail's. A schedule's runs carry nothing. A unique push that pushes nothing, or a latest one that moves the job that waits, leaves the job what its own push carried. It's a few strings, by name, at most 4 KB as JSON: a push that would carry more fails. - In the queue's log lines of a job: each has what the job carried, by name, after its kind, ID and the rest, as
request_idabove. Job.Carried, andqueue.CarryStore: a Store keeps what a job carried when it's aCarryStore, an extra, asAtOnceStoreis, with a marker method,KeepsCarried: its pushes keep it, and its claims andFailedgive it back. A job pushed to a Store that isn't one carries nothing, and runs as before.queuetest.TestStorechecks the promise of a Store that makes it, andqueuetest.Memorydoes.middleware.CarryRequestID: the Carrier of the IDRequestIDgives a request, asrequest_id, which gives back only an ID as plain asRequestIDkeeps one, as it's been in the database; andmiddleware.WithRequestID, which puts an ID in a context, forRequestIDFromto read.- The auth starter's queue carries the request's ID: in a
carriedcolumn of its jobs table, which a migration adds in each database, and itsjobscommand, and its admins' page of the jobs that failed, in all three frontends, say which request pushed each. - The guide: Background jobs, a section on what a job carries; Deployment, a job's log lines matched to its request's; Routing and Accounts, where the ID goes; and the README.
Upgrading: nothing an app calls changed: go get github.com/cuonggt/tug@v0.34.0, and a queue without Carry runs as it did. An app made with tug new -auth before keeps its jobs table and its Store as they are, and its tests pass, as TestStore checks what's carried only of a CarryStore. To carry the request's ID, its queue's Carry lists middleware.CarryRequestID, and its jobs table keeps what's carried: a migration adds the column, ALTER TABLE jobs ADD COLUMN carried TEXT;, and the Store writes it as it pushes, reads it back as it claims and lists the failed, and says so with KeepsCarried, as a new app's jobs.go and jobs_db.go do. Carry without the Store's part carries nothing.
The guide is in docs/. tug needs Go 1.26.