{"id":20136986,"url":"https://github.com/txix-open/bgjob","last_synced_at":"2026-04-16T00:32:00.300Z","repository":{"id":46105054,"uuid":"426671312","full_name":"txix-open/bgjob","owner":"txix-open","description":"Tiny library to handle background jobs.","archived":false,"fork":false,"pushed_at":"2025-10-28T08:36:35.000Z","size":46,"stargazers_count":3,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-10-28T10:21:02.786Z","etag":null,"topics":["background-jobs","dlq-retry","go","golang","library","postgresql","worker"],"latest_commit_sha":null,"homepage":"","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/txix-open.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2021-11-10T15:18:23.000Z","updated_at":"2025-10-28T08:32:16.000Z","dependencies_parsed_at":"2025-10-28T10:12:38.799Z","dependency_job_id":"b28d835d-cf0c-4ff3-840d-e9f10dd37d8e","html_url":"https://github.com/txix-open/bgjob","commit_stats":null,"previous_names":["txix-open/bgjob","integration-system/bgjob"],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/txix-open/bgjob","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/txix-open%2Fbgjob","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/txix-open%2Fbgjob/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/txix-open%2Fbgjob/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/txix-open%2Fbgjob/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/txix-open","download_url":"https://codeload.github.com/txix-open/bgjob/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/txix-open%2Fbgjob/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31866286,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-15T15:24:51.572Z","status":"ssl_error","status_checked_at":"2026-04-15T15:24:39.138Z","response_time":63,"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":["background-jobs","dlq-retry","go","golang","library","postgresql","worker"],"created_at":"2024-11-13T21:24:40.871Z","updated_at":"2026-04-16T00:31:59.945Z","avatar_url":"https://github.com/txix-open.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# bgjob\n![Build and test](https://github.com/txix-open/bgjob/actions/workflows/main.yml/badge.svg)\n[![codecov](https://codecov.io/gh/txix-open/bgjob/branch/master/graph/badge.svg?token=ZEX2Y8ZWKZ)](https://codecov.io/gh/txix-open/bgjob)\n[![Go Report Card](https://goreportcard.com/badge/github.com/txix-open/bgjob)](https://goreportcard.com/report/github.com/txix-open/bgjob)\n\nTiny library to handle background jobs.\n\nUses PostgreSQL to organize job queues.\n\nHighly inspired by [gue](https://github.com/vgarvardt/gue)\n\n## Features\n* Durable job storage\n* At-least-ones execution\n* Job uniqueness\n* Delayed execution\n* Retries with dynamic timeout\n* [Enqueuing jobs in transaction](#enqueue-job-in-transaction)\n* DLQ\n* Zero dependencies (database/sql is used, to log events you can use `Observer`)\n\n## Why ? \n* You need flexible and robust job processing tool\n* You already use PostgreSQL\n* You require strong guaranties for task processing and consistency\n* You have a quite small load. Queues on database usually can handle around 1000 rps\n\n## State\n* The package has been used in production for 3 years with a small load\n\n## Install\n1. ```go get github.com/txix-open/bgjob```\n2. Add to your db migration tool sql from migration/init.sql\n\n## Complete example\n```go\npackage main\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"errors\"\n\t\"fmt\"\n\t\"log\"\n\t\"runtime\"\n\t\"time\"\n\n\t_ \"github.com/jackc/pgx/v5/stdlib\"\n\n\t\"github.com/txix-open/bgjob\"\n)\n\ntype Observer struct {\n\tbgjob.NoopObserver //\"extend\" NoopObserver to override method you need\n}\n\nfunc (o Observer) WorkerError(ctx context.Context, err error) {\n\tlog.Printf(\"worker: %v\", err)\n}\n\nfunc main() {\n\tdsn := \"postgres://test:test@localhost:5432/test\"\n\tdb, err := sql.Open(\"pgx\", dsn) //be sure you add tables and indexes from migration/init.sql\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\thandleComplete := bgjob.HandlerFunc(func(ctx context.Context, job bgjob.Job) bgjob.Result {\n\t\tfmt.Printf(\"%s\\n\", job.Arg)\n\t\treturn bgjob.Complete() //complete job, job will be deleted\n\t})\n\thandleRetry := bgjob.HandlerFunc(func(ctx context.Context, job bgjob.Job) bgjob.Result {\n\t\tif job.Attempt == 2 {\n\t\t\treturn bgjob.Complete()\n\t\t}\n\t\t//here you have attempts counter, you can easily do backoff retries\n\t\treturn bgjob.Retry(1*time.Second, errors.New(\"some error\"))\n\t})\n\thandleMoveToDlq := bgjob.HandlerFunc(func(ctx context.Context, job bgjob.Job) bgjob.Result {\n\t\t//you can move the job to permanent storage if you can't handle it\n\t\treturn bgjob.MoveToDlq(errors.New(\"move to dlq\"))\n\t})\n\t\n\tctx := context.Background()\n\tstore, err := bgjob.NewPgStoreV2(ctx, db)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tcli := bgjob.NewClient(store)\n\n\t//if handler for job type wasn't provided, job will be moved to dlq\n\thandler := bgjob.NewMux().\n\t\tRegister(\"complete_me\", handleComplete).\n\t\tRegister(\"retry_me\", handleRetry).\n\t\tRegister(\"move_to_dlq\", handleMoveToDlq)\n\tqueueName := \"test\"\n\tobserver := Observer{}\n\tworker := bgjob.NewWorker(\n\t\tcli,\n\t\tqueueName,\n\t\thandler,\n\t\tbgjob.WithObserver(observer),            //default noop\n\t\tbgjob.WithConcurrency(runtime.NumCPU()), //default 1\n\t\tbgjob.WithPollInterval(500*time.Millisecond), //default 1s\n\t)\n\tworker.Run(ctx) //call ones, non-blocking\n\n\terr = cli.Enqueue(ctx, bgjob.EnqueueRequest{\n\t\tId:    \"test\", //you can provide you own id\n\t\tQueue: queueName,\n\t\tType:  \"complete_me\",\n\t\tArg:   []byte(`{\"simpleJson\": 1}`), //it can be json or protobuf or a simple string\n\t\tDelay: 1 * time.Second,             //you can delay job execution\n\t})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\terr = cli.Enqueue(ctx, bgjob.EnqueueRequest{\n\t\tQueue: queueName,\n\t\tType:  \"retry_me\",\n\t\t//those fields must be specified\n\t})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\terr = cli.Enqueue(ctx, bgjob.EnqueueRequest{\n\t\tQueue: queueName,\n\t\tType:  \"move_to_dlq\",\n\t})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\ttime.Sleep(1 * time.Second)\n\tworker.Shutdown() //graceful shutdown, call ones\n}\n\n\n```\n\n## Enqueue job in transaction\n* `bgjob.Enqueue` and `bgjob.BulkEnqueue` can help\n\n```go\npackage main\n\nfunc enqueueInTx(db *sql.DB) (err error) {\n\ttx, err := db.Begin()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"begin tx: %w\", err)\n\t}\n\tdefer func() {\n\t\tif err != nil {\n\t\t\t_ = tx.Rollback()\n\t\t}\n\t}()\n\t\n\t// YOUR BUSINESS TRANSACTION HERE\n\t\n\terr = bgjob.Enqueue(context.Background(), tx, bgjob.EnqueueRequest{\n\t\tQueue: \"work\",\n\t\tType:  \"send_email\",\n\t})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"begin tx: %w\", err)\n\t}\n\t\n\terr = tx.Commit()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"commit tx: %w\", err)\n\t}\n\t\n\treturn nil\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftxix-open%2Fbgjob","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftxix-open%2Fbgjob","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftxix-open%2Fbgjob/lists"}