{"id":13490924,"url":"https://github.com/riverqueue/river","last_synced_at":"2026-02-22T19:00:55.073Z","repository":{"id":206463343,"uuid":"716228772","full_name":"riverqueue/river","owner":"riverqueue","description":"Fast and reliable background jobs in Go","archived":false,"fork":false,"pushed_at":"2026-02-15T20:26:27.000Z","size":3602,"stargazers_count":4818,"open_issues_count":41,"forks_count":137,"subscribers_count":23,"default_branch":"master","last_synced_at":"2026-02-15T21:51:19.226Z","etag":null,"topics":["background-jobs","go","golang","postgres","postgresql","queue"],"latest_commit_sha":null,"homepage":"https://riverqueue.com","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/riverqueue.png","metadata":{"files":{"readme":"docs/README.md","changelog":"CHANGELOG.md","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":"2023-11-08T17:43:41.000Z","updated_at":"2026-02-15T20:26:29.000Z","dependencies_parsed_at":"2026-02-15T17:02:51.912Z","dependency_job_id":null,"html_url":"https://github.com/riverqueue/river","commit_stats":{"total_commits":369,"total_committers":22,"mean_commits":"16.772727272727273","dds":0.3577235772357723,"last_synced_commit":"e9b751fc9fbcd81213f768aa7ac2c90b87f7b439"},"previous_names":["riverqueue/river"],"tags_count":450,"template":false,"template_full_name":null,"purl":"pkg:github/riverqueue/river","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/riverqueue%2Friver","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/riverqueue%2Friver/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/riverqueue%2Friver/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/riverqueue%2Friver/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/riverqueue","download_url":"https://codeload.github.com/riverqueue/river/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/riverqueue%2Friver/sbom","scorecard":{"id":135746,"data":{"date":"2025-08-04","repo":{"name":"github.com/riverqueue/river","commit":"5980899509bbbcee177366bc7b998fa6045ec351"},"scorecard":{"version":"v5.2.1-28-gc1d103a9","commit":"c1d103a9bb9f635ec7260bf9aa0699466fa4be0e"},"score":5.9,"checks":[{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":10,"reason":"all changesets reviewed","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#code-review"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#packaging"}},{"name":"Maintained","score":10,"reason":"30 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#maintained"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#cii-best-practices"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#binary-artifacts"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Info: jobLevel 'contents' permission set to 'read': .github/workflows/ci.yaml:248","Info: jobLevel 'pull-requests' permission set to 'read': .github/workflows/ci.yaml:250","Warn: no topLevel permission defined: .github/workflows/ci.yaml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":1,"reason":"dependency not pinned by hash detected -- score normalized to 1","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yaml:62: update your workflow using https://app.stepsecurity.io/secureworkflow/riverqueue/river/ci.yaml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yaml:65: update your workflow using https://app.stepsecurity.io/secureworkflow/riverqueue/river/ci.yaml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/ci.yaml:87: update your workflow using https://app.stepsecurity.io/secureworkflow/riverqueue/river/ci.yaml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yaml:92: update your workflow using https://app.stepsecurity.io/secureworkflow/riverqueue/river/ci.yaml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yaml:113: update your workflow using https://app.stepsecurity.io/secureworkflow/riverqueue/river/ci.yaml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yaml:253: update your workflow using https://app.stepsecurity.io/secureworkflow/riverqueue/river/ci.yaml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yaml:259: update your workflow using https://app.stepsecurity.io/secureworkflow/riverqueue/river/ci.yaml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/ci.yaml:262: update your workflow using https://app.stepsecurity.io/secureworkflow/riverqueue/river/ci.yaml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yaml:283: update your workflow using https://app.stepsecurity.io/secureworkflow/riverqueue/river/ci.yaml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/ci.yaml:286: update your workflow using https://app.stepsecurity.io/secureworkflow/riverqueue/river/ci.yaml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yaml:304: update your workflow using https://app.stepsecurity.io/secureworkflow/riverqueue/river/ci.yaml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yaml:310: update your workflow using https://app.stepsecurity.io/secureworkflow/riverqueue/river/ci.yaml/master?enable=pin","Info:   0 out of   9 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   3 third-party GitHubAction dependencies pinned","Info:   1 out of   1 goCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#pinned-dependencies"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: Mozilla Public License 2.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#license"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#fuzzing"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#branch-protection"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#security-policy"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#signed-releases"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 30 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/c1d103a9bb9f635ec7260bf9aa0699466fa4be0e/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-16T06:27:17.275Z","repository_id":206463343,"created_at":"2025-08-16T06:27:17.275Z","updated_at":"2025-08-16T06:27:17.275Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29552224,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-17T17:56:56.811Z","status":"ssl_error","status_checked_at":"2026-02-17T17:56:55.544Z","response_time":100,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: 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","go","golang","postgres","postgresql","queue"],"created_at":"2024-07-31T19:00:52.114Z","updated_at":"2026-02-22T19:00:55.067Z","avatar_url":"https://github.com/riverqueue.png","language":"Go","readme":"# River [![Build Status](https://github.com/riverqueue/river/actions/workflows/ci.yaml/badge.svg?branch=master)](https://github.com/riverqueue/river/actions) [![Go Reference](https://pkg.go.dev/badge/github.com/riverqueue/river.svg)](https://pkg.go.dev/github.com/riverqueue/river)\n\nRiver is a robust high-performance job processing system for Go and Postgres.\n\nSee [homepage], [docs], and [godoc], as well as the [River UI][riverui] and [its\nlive demo][riveruidemo].\n\nBeing built for Postgres, River encourages the use of the same database for\napplication data and job queue. By enqueueing jobs transactionally along with\nother database changes, whole classes of distributed systems problems are\navoided. Jobs are guaranteed to be enqueued if their transaction commits, are\nremoved if their transaction rolls back, and aren't visible for work _until_\ncommit. See [transactional enqueueing] for more background on this philosophy.\n\n## Job args and workers\n\nJobs are defined in struct pairs, with an implementation of [`JobArgs`] and one\nof [`Worker`].\n\nJob args contain `json` annotations and define how jobs are serialized to and\nfrom the database, along with a \"kind\", a stable string that uniquely identifies\nthe job.\n\n```go\ntype SortArgs struct {\n    // Strings is a slice of strings to sort.\n    Strings []string `json:\"strings\"`\n}\n\nfunc (SortArgs) Kind() string { return \"sort\" }\n```\n\nWorkers expose a `Work` function that dictates how jobs run.\n\n```go\ntype SortWorker struct {\n    // An embedded WorkerDefaults sets up default methods to fulfill the rest of\n    // the Worker interface:\n    river.WorkerDefaults[SortArgs]\n}\n\nfunc (w *SortWorker) Work(ctx context.Context, job *river.Job[SortArgs]) error {\n    sort.Strings(job.Args.Strings)\n    fmt.Printf(\"Sorted strings: %+v\\n\", job.Args.Strings)\n    return nil\n}\n```\n\n## Registering workers\n\nJobs are uniquely identified by their \"kind\" string. Workers are registered on\nstart up so that River knows how to assign jobs to workers:\n\n```go\nworkers := river.NewWorkers()\n// AddWorker panics if the worker is already registered or invalid:\nriver.AddWorker(workers, \u0026SortWorker{})\n```\n\n## Starting a client\n\nA River [`Client`] provides an interface for job insertion and manages job\nprocessing and [maintenance services]. A client's created with a database pool,\n[driver], and config struct containing a `Workers` bundle and other settings.\nHere's a `Client` working one queue (`\"default\"`) with up to 100 worker\ngoroutines at a time:\n\n```go\nriverClient, err := river.NewClient(riverpgxv5.New(dbPool), \u0026river.Config{\n    Queues: map[string]river.QueueConfig{\n        river.QueueDefault: {MaxWorkers: 100},\n    },\n    Workers: workers,\n})\nif err != nil {\n    panic(err)\n}\n\n// Run the client inline. All executed jobs will inherit from ctx:\nif err := riverClient.Start(ctx); err != nil {\n    panic(err)\n}\n```\n\n## Insert-only clients\n\nIt's often desirable to have a client that'll be used for inserting jobs, but\nnot working them. This is possible by omitting the `Queues` configuration, and\nskipping the call to `Start`:\n\n```go\nriverClient, err := river.NewClient(riverpgxv5.New(dbPool), \u0026river.Config{\n    Workers: workers,\n})\nif err != nil {\n    panic(err)\n}\n```\n\n`Workers` can also be omitted, but it's better to include it so River can check\nthat inserted job kinds have a worker that can run them.\n\n### Stopping\n\nThe client should also be stopped on program shutdown:\n\n```go\n// Stop fetching new work and wait for active jobs to finish.\nif err := riverClient.Stop(ctx); err != nil {\n    panic(err)\n}\n```\n\nThere are some complexities around ensuring clients stop cleanly, but also in a\ntimely manner. See [graceful shutdown] for more details on River's stop modes.\n\n## Inserting jobs\n\n[`Client.InsertTx`] is used in conjunction with an instance of job args to\ninsert a job to work on a transaction:\n\n```go\n_, err = riverClient.InsertTx(ctx, tx, SortArgs{\n    Strings: []string{\n        \"whale\", \"tiger\", \"bear\",\n    },\n}, nil)\n\nif err != nil {\n    panic(err)\n}\n```\n\nSee the [`InsertAndWork` example] for complete code.\n\n## Other features\n\n  - [Batch job insertion] for efficiently inserting many jobs at once using\n    Postgres `COPY FROM`.\n\n  - [Cancelling jobs] from inside a work function.\n\n  - [Error and panic handling].\n\n  - [Multiple queues] to better guarantee job throughput, worker availability,\n    and isolation between components.\n\n  - [Periodic and cron jobs].\n\n  - [Scheduled jobs] that run automatically at their scheduled time in the\n    future.\n\n  - [Snoozing jobs] from inside a work function.\n\n  - [Subscriptions] to queue activity and statistics, providing easy hooks for\n    telemetry like logging and metrics.\n\n  - [Test helpers] to verify that jobs are inserted as expected.\n\n  - [Transactional job completion] to guarantee job completion commits with\n    other changes in a transaction.\n\n  - [Unique jobs] by args, period, queue, and state.\n\n  - [Web UI] for inspecting and interacting with jobs and queues.\n\n  - [Work functions] for simplified worker implementation.\n\n## Cross language enqueueing\n\nRiver supports inserting jobs in some non-Go languages which are then worked by Go implementations. This may be desirable in performance sensitive cases so that jobs can take advantage of Go's fast runtime.\n\n  - [Inserting jobs from Python](https://riverqueue.com/docs/python).\n  - [Inserting jobs from Ruby](https://riverqueue.com/docs/ruby).\n\n## Development\n\nSee [developing River].\n\n## Thank you\n\nRiver was in large part inspired by our experiences with other background job libraries over the years, most notably:\n\n- [Oban](https://github.com/sorentwo/oban) in Elixir.\n- [Que](https://github.com/que-rb/que), [Sidekiq](https://github.com/sidekiq/sidekiq), [Delayed::Job](https://github.com/collectiveidea/delayed_job), and [GoodJob](https://github.com/bensheldon/good_job) in Ruby.\n- [Hangfire](https://www.hangfire.io/) in .NET.\n\nThank you for driving the software ecosystem forward.\n\n[`Client`]: https://pkg.go.dev/github.com/riverqueue/river#Client\n[`Client.InsertTx`]: https://pkg.go.dev/github.com/riverqueue/river#Client.InsertTx\n[`InsertAndWork` example]: https://pkg.go.dev/github.com/riverqueue/river#example-package-InsertAndWork\n[`JobArgs`]: https://pkg.go.dev/github.com/riverqueue/river#JobArgs\n[`Worker`]: https://pkg.go.dev/github.com/riverqueue/river#Worker\n[Batch job insertion]: https://riverqueue.com/docs/batch-job-insertion\n[Cancelling jobs]: https://riverqueue.com/docs/cancelling-jobs\n[Error and panic handling]: https://riverqueue.com/docs/error-handling\n[Multiple queues]: https://riverqueue.com/docs/multiple-queues\n[Periodic and cron jobs]: https://riverqueue.com/docs/periodic-jobs\n[Scheduled jobs]: https://riverqueue.com/docs/scheduled-jobs\n[Snoozing jobs]: https://riverqueue.com/docs/snoozing-jobs\n[Subscriptions]: https://riverqueue.com/docs/subscriptions\n[Test helpers]: https://riverqueue.com/docs/testing\n[Transactional job completion]: https://riverqueue.com/docs/transactional-job-completion\n[Unique jobs]: https://riverqueue.com/docs/unique-jobs\n[Web UI]: https://github.com/riverqueue/riverui\n[Work functions]: https://riverqueue.com/docs/work-functions\n[developing River]: https://github.com/riverqueue/river/blob/master/docs/development.md\n[docs]: https://riverqueue.com/docs\n[driver]: https://riverqueue.com/docs/database-drivers\n[godoc]: https://pkg.go.dev/github.com/riverqueue/river\n[graceful shutdown]: https://riverqueue.com/docs/graceful-shutdown\n[homepage]: https://riverqueue.com\n[maintenance services]: https://riverqueue.com/docs/maintenance-services\n[riverui]: https://github.com/riverqueue/riverui\n[riveruidemo]: https://ui.riverqueue.com\n[transactional enqueueing]: https://riverqueue.com/docs/transactional-enqueueing\n","funding_links":[],"categories":["Go","\u003ca name=\"Go\"\u003e\u003c/a\u003eGo","Work Queues"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Friverqueue%2Friver","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Friverqueue%2Friver","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Friverqueue%2Friver/lists"}