{"id":25445798,"url":"https://github.com/divmgl/go-jackd","last_synced_at":"2026-03-01T12:02:14.628Z","repository":{"id":57647142,"uuid":"442531551","full_name":"divmgl/go-jackd","owner":"divmgl","description":"Simple and modern beanstalkd library for Golang","archived":false,"fork":false,"pushed_at":"2025-04-07T03:01:59.000Z","size":48,"stargazers_count":8,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-01-14T23:24:49.987Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/divmgl.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-12-28T17:10:46.000Z","updated_at":"2025-04-07T02:41:55.000Z","dependencies_parsed_at":"2022-09-14T21:11:49.493Z","dependency_job_id":null,"html_url":"https://github.com/divmgl/go-jackd","commit_stats":null,"previous_names":["divmgl/go-jackd","getjackd/go-jackd"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/divmgl/go-jackd","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/divmgl%2Fgo-jackd","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/divmgl%2Fgo-jackd/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/divmgl%2Fgo-jackd/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/divmgl%2Fgo-jackd/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/divmgl","download_url":"https://codeload.github.com/divmgl/go-jackd/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/divmgl%2Fgo-jackd/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29969243,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-01T11:43:06.159Z","status":"ssl_error","status_checked_at":"2026-03-01T11:43:03.887Z","response_time":124,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-02-17T16:50:17.668Z","updated_at":"2026-03-01T12:02:14.615Z","avatar_url":"https://github.com/divmgl.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# go-jackd\n\n[![Go Tests](https://github.com/divmgl/go-jackd/actions/workflows/test.yml/badge.svg)](https://github.com/divmgl/go-jackd/actions/workflows/test.yml)\n\n\n```go\npackage main\n\nimport (\n\t\"context\"\n\t\"log\"\n\t\"github.com/getjackd/go-jackd\"\n)\n\nfunc main() {\n\tctx := context.Background()\n\tconn := jackd.Must(jackd.Dial(ctx, \"localhost:11300\")) // =\u003e *jackd.Client\n\n\t// Producing\n\tconn.Put(ctx, []byte(\"Hello!\"), jackd.DefaultPutOpts())\n\n\t// Consuming\n\tid, payload, err := conn.Reserve(ctx)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t// Process the job then delete it\n\tconn.Delete(ctx, id)\n}\n```\n\n## Why\n\n`go-jackd` was inspired by the original `jackd` client made for Node.js and it follows the same principles:\n\n- Concise and easy to use API\n- No dependencies\n- Protocol accuracy/completeness\n\nMost, if not all, Go `beanstalkd` clients are out of date (`gobeanstalk`, `gostalk`), are missing features (`go-beanstalk` does not have the `reserve` command) and some don't handle certain use-cases, such as:\n\n- Job payloads that are bigger than the current TCP frame\n- Line breaks in job payloads that match the `beanstalkd` delimiter\n\n## Overview\n\n`beanstalkd` is a simple and blazing fast work queue. Producers connected through TCP sockets send in jobs to be processed at a later time by a consumer.\n\nIf you don't have experience using `beanstalkd`, [it's a good idea to read the `beanstalkd` protocol before using this library.](https://github.com/beanstalkd/beanstalkd/blob/master/doc/protocol.txt)\n\n### Connecting and disconnecting\n\n```go\nctx := context.Background()\nconn, err := jackd.Dial(ctx, \"localhost:11300\")\nif err != nil {\n\t// Handle error\n}\n\n// If you just want to fail in the case of any error, such as when your\n// code simply won't work if a connection to beanstalkd can't be made:\nconn := jackd.Must(jackd.Dial(ctx, \"localhost:11300\"))\n\n// Disconnect\nconn.Quit(ctx)\n```\n\n### Context Support\n\n`go-jackd` provides first-class support for Go's context package. All operations accept a context parameter which can be used for:\n\n- Cancellation: Cancel long-running operations like `Reserve`\n- Deadlines: Set timeouts for operations\n- Values: Pass request-scoped values\n\nExample with timeout:\n```go\nctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\ndefer cancel()\n\n// This will timeout after 5 seconds if no job is available\nid, payload, err := conn.Reserve(ctx)\nif err == context.DeadlineExceeded {\n\t// Handle timeout\n}\n```\n\n### Producers\n\n#### Adding jobs to a tube\n\nYou can add jobs to a tube by using the `put` command, which accepts a payload and returns a job ID.\n\n```go\nconn.Put(ctx, []byte(\"my long running job\"), jackd.DefaultPutOpts())\n```\n\nAll jobs sent to `beanstalkd` have a priority, a delay, and TTR (time-to-run) specification. `jackd` provides a `DefaultPutOpts()` function that returns a new `PutOpts` struct with `0` priority, `0` delay, and `60` TTR, which means consumers will have 60 seconds to finish the job after reservation. You can override these defaults:\n\n```go\nopts := PutOpts{\n\tPriority: 0,\n\tDelay: 2 * time.Minute, // delays for two minutes\n\tTTR: 10 * time.Minute, // jobs can be reserved for ten minutes\n}\n\nconn.Put(ctx, []byte(\"delayed job\"), opts)\n```\n\nJobs with lower priorities are handled first. Refer to [the protocol specs](https://github.com/beanstalkd/beanstalkd/blob/master/doc/protocol.txt#L126) for more information on job options.\n\n#### Using different tubes\n\nAll jobs are added to the `default` tube by default. You can change the tube to send jobs to with `use`.\n\n```go\ntubeName, err := conn.Use(ctx, \"awesome-tube\") // tubeName == \"awesome-tube\"\nconn.Put(ctx, []byte(\"awesome job\"), jackd.DefaultPutOpts()) // job is put into awesome-tube\n```\n\n### Consumers\n\n#### Reserving a job\n\nConsumers work by reserving jobs in a tube. Reserving is a blocking operation and execution will stop until a job has been reserved or the context is cancelled.\n\n```go\nid, payload, err := conn.Reserve(ctx) // Reserve the next job\n```\n\n#### Reserving specific jobs (1.12+)\n\nYou can also reserve specific jobs as of `beanstalkd` 1.12. This command will simply fail in older versions.\n\n```go\nid, payload, err := conn.PeekReady(ctx) // PeekReady returns the payload\n_, _, err := conn.ReserveJob(ctx, id)\n```\n\n#### Performing job operations\n\nOnce you've reserved a job, there are several operations you can perform on it. The most common operation will be deleting the job after the consumer is finished processing it.\n\n```go\nerr := conn.Delete(ctx, id)\n```\n\nConsumers can also give up their reservation by releasing the job. You'll usually want to release the job if an error occurred on the consumer and you want to put it back in the queue immediately.\n\n```go\n// Release immediately with high priority (0) and no delay (0)\nerr := conn.Release(ctx, id, jackd.DefaultReleaseOpts())\n\n// Release with a priority and delay\nopts := ReleaseOpts{\n\tPriority: 10,\n\tDelay: 10 * time.Second // release after 10 seconds\n}\nerr := conn.Release(ctx, id, opts)\n```\n\nHowever, you may want to bury the job to be processed later under certain conditions, such as a recurring error or a job that can't be processed. Buried jobs will not be processed until they are kicked.\n\n```go\nerr := conn.Bury(ctx, id, /* priority */ 0)\n// ...some time later...\nerr := conn.KickJob(ctx, id)\n```\n\nYou'll notice that the kick operation is suffixed by `Job`. This is because there is a `kick` command in `beanstalkd` which will kick a certain number of jobs back into the tube.\n\n```go\n// beanstalkd will attempt to kick 100 jobs. The number of jobs kicked will be returned\nnumKicked, err := conn.Kick(ctx, 100)\n```\n\nConsumers will sometimes need additional time to run jobs. You can `touch` those jobs to let `beanstalkd` know you're still processing them.\n\n```go\nerr := conn.Touch(ctx, id)\n```\n\n#### Watching on multiple tubes\n\nBy default, all consumers will watch the `default` tube only. Consumers can elect what tubes they want to watch.\n\n```go\nnumWatched, err := conn.Watch(ctx, \"my-special-tube\")\n```\n\nIf a consumer is watching a tube and it no longer needs it, you can choose to ignore that tube as well.\n\n```go\nnumWatched, err := conn.Ignore(ctx, \"default\")\n```\n\nPlease keep in mind that attempting to ignore the only tube being watched will result in an error.\n\nYou can also bring back the current tubes watched using `list-tubes-watched`. However, please keep in mind that this command returns YAML, so you'll need to parse it.\n\n```go\nimport \"github.com/goccy/go-yaml\"\n\nresp, err := conn.ListTubesWatched()\n\nvar tubes []string\nerr = yaml.Unmarshal(resp, \u0026tubes)\n```\n\n### All commands are available\n\n`go-jackd` has first class support for all `beanstalkd` commands. Please refer to the [`beanstalkd` protocol](https://github.com/beanstalkd/beanstalkd/blob/master/doc/protocol.txt) for a complete list of commands.\n\n## Worker pattern\n\nHere's an example implementation of a worker pattern with proper context handling:\n\n```go\npackage main\n\nimport (\n\t\"context\"\n\t\"log\"\n\t\"time\"\n\n\t\"github.com/getjackd/go-jackd\"\n)\n\nfunc main() {\n\tctx := context.Background()\n\tconn := jackd.Must(jackd.Dial(ctx, \"localhost:11300\"))\n\n\terr := worker(ctx, conn, func(ctx context.Context, id uint32, body []byte) error {\n\t\t// ...process the job body...\n\n\t\t// ...then delete the job once we're finished.\n\t\tif err := conn.Delete(ctx, id); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\treturn nil\n\t})\n\n\tlog.Fatal(err)\n}\n\nfunc worker(ctx context.Context, conn *jackd.Client, fn func(ctx context.Context, id uint32, body []byte) error) error {\n\tfor {\n\t\t// Create a context with timeout for each reservation\n\t\treserveCtx, cancel := context.WithTimeout(ctx, 30*time.Second)\n\t\tid, body, err := conn.Reserve(reserveCtx)\n\t\tcancel()\n\n\t\tif err != nil {\n\t\t\tif err == context.DeadlineExceeded {\n\t\t\t\t// Timeout is expected, continue to next iteration\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\t// If we're unable to reserve jobs, something's gone wrong. Quit\n\t\t\t// the consumer.\n\t\t\treturn err\n\t\t}\n\n\t\t// Create a new context for job processing with the job's TTR\n\t\tjobCtx, cancel := context.WithTimeout(ctx, 60*time.Second)\n\t\tif err := fn(jobCtx, id, body); err != nil {\n\t\t\tlog.Printf(\"unable to process job %d: %+v\", id, err)\n\n\t\t\terr = conn.Bury(jobCtx, id, 0)\n\t\t\tcancel()\n\t\t\tif err != nil {\n\t\t\t\t// If we're having issues burying errored jobs, something's gone\n\t\t\t\t// wrong. quit the consumer\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\tcancel()\n\t}\n}\n```\n\n## Concurrency\n\n`jackd` as of 1.1.0 supports issuing commands from multiple goroutines. In order to avoid concurrency issues, all `jackd` commands are synchronized with a mutex. This is because `beanstalkd` processes commands per connection serially. \n\nPlease keep this in mind as your goroutines may block each other if they're utilizing the same `jackd` instance (especially with long-running commands, like the `reserve` commands). This is normally not a problem in most architectures, but if you do run into issues, you have several options:\n\n* If you need to publish and consume from the same process, use two separate `jackd` instances: one for publishing and one for consuming \n* Ensure that you create individual `jackd` instances per goroutine. Keep in mind that this opens a new connection to `beanstalkd`.\n* Keep all of your code synchronous when dealing with `jackd` (specifically, use mutexes, wait groups, or simply do not use multiple goroutines with `jackd`)\n\n# License\n\nMIT","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdivmgl%2Fgo-jackd","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdivmgl%2Fgo-jackd","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdivmgl%2Fgo-jackd/lists"}