{"id":13413036,"url":"https://github.com/vivek-ng/concurrency-limiter","last_synced_at":"2026-04-02T18:32:42.895Z","repository":{"id":56074252,"uuid":"314944787","full_name":"vivek-ng/concurrency-limiter","owner":"vivek-ng","description":null,"archived":false,"fork":false,"pushed_at":"2026-03-29T23:40:40.000Z","size":75,"stargazers_count":18,"open_issues_count":0,"forks_count":5,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-03-30T00:54:21.506Z","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/vivek-ng.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":"2020-11-22T02:35:52.000Z","updated_at":"2026-03-29T23:40:16.000Z","dependencies_parsed_at":"2023-01-31T09:01:21.718Z","dependency_job_id":null,"html_url":"https://github.com/vivek-ng/concurrency-limiter","commit_stats":null,"previous_names":[],"tags_count":32,"template":false,"template_full_name":null,"purl":"pkg:github/vivek-ng/concurrency-limiter","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vivek-ng%2Fconcurrency-limiter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vivek-ng%2Fconcurrency-limiter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vivek-ng%2Fconcurrency-limiter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vivek-ng%2Fconcurrency-limiter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vivek-ng","download_url":"https://codeload.github.com/vivek-ng/concurrency-limiter/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vivek-ng%2Fconcurrency-limiter/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31312917,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-02T12:59:32.332Z","status":"ssl_error","status_checked_at":"2026-04-02T12:54:48.875Z","response_time":89,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5: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":[],"created_at":"2024-07-30T20:01:32.628Z","updated_at":"2026-04-02T18:32:42.886Z","avatar_url":"https://github.com/vivek-ng.png","language":"Go","funding_links":[],"categories":["Goroutines","Relational Databases"],"sub_categories":["Search and Analytic Databases","检索及分析资料库","Advanced Console UIs"],"readme":"# concurrency-limiter\n\n[![Mentioned in Awesome Go](https://awesome.re/mentioned-badge.svg)](https://github.com/avelino/awesome-go)\n[![GoDoc reference example](https://img.shields.io/badge/godoc-reference-blue.svg)](https://godoc.org/github.com/vivek-ng/concurrency-limiter)\n[![GoReportCard example](https://goreportcard.com/badge/github.com/nanomsg/mangos)](https://goreportcard.com/report/github.com/vivek-ng/concurrency-limiter)\n[![codecov](https://codecov.io/gh/vivek-ng/concurrency-limiter/branch/main/graph/badge.svg?token=UFN7OqUNDH)](https://codecov.io/gh/vivek-ng/concurrency-limiter)\n\n## About\n\nconcurrency-limiter allows you to limit the number of goroutines accessing a resource with support for\ntimeouts , dynamic priority of goroutines and context cancellation of goroutines.\n\nThe library now supports two admission modes:\n- strict admission via `Wait` / `Run`\n- soft admission via `WaitOrBypass` / `RunOrBypass`\n\n### Installation\n\nTo install concurrency-limiter:\n\n```\ngo get github.com/vivek-ng/concurrency-limiter\n```\n\nThen import concurrency-limiter to use it\n\n```go\n    import(\n        \"time\"\n        github.com/vivek-ng/concurrency-limiter/limiter\n    )\n\n    nl := limiter.New(3)\n    ctx := context.Background()\n    if err := nl.Wait(ctx); err != nil {\n        return\n    }\n    // Perform actions .........\n    nl.Finish()\n\n```\n\n\n## Usage\n\nBelow are some examples of using this library. To run real examples , please check the examples folder.\n\n### Limiter\n\n```go\n    import(\n        \"time\"\n        github.com/vivek-ng/concurrency-limiter/limiter\n    )\n\n    func main() {\n        nl := limiter.New(3)\n\n        var wg sync.WaitGroup\n        wg.Add(15)\n\n        for i := 0; i \u003c 15; i++ {\n            go func(index int) {\n                defer wg.Done()\n                ctx := context.Background()\n                if err := nl.Wait(ctx); err != nil {\n                    return\n                }\n                // in real life , this can be DB operations , message publish to queue ........\n                fmt.Println(\"executing action...: \", \"index: \", index, \"current number of goroutines: \", nl.Count())\n                nl.Finish()\n            }(i)\n        }\n        wg.Wait()\n    }\n```\n\n### Priority Limiter\n\n```go\n    import(\n        \"time\"\n        github.com/vivek-ng/concurrency-limiter/priority\n    )\n    \n    func main() {\n        pr := priority.NewLimiter(1)\n        var wg sync.WaitGroup\n        wg.Add(15)\n        for i := 0; i \u003c 15; i++ {\n            go func(index int) {\n                defer wg.Done()\n                ctx := context.Background()\n                if index%2 == 1 {\n                    if err := pr.Wait(ctx, priority.High); err != nil {\n                        return\n                    }\n                } else {\n                    if err := pr.Wait(ctx, priority.Low); err != nil {\n                        return\n                    }\n                }\n                // in real life , this can be DB operations , message publish to queue ........\n                fmt.Println(\"executing action...: \", \"index: \", index, \"current number of goroutines: \", pr.Count())\n                pr.Finish()\n            }(i)\n        }\n        wg.Wait()\n    }\n```\n\n## Examples\n\n### Limiter\n\n```go\n    nl := limiter.New(3)\n    ctx := context.Background()\n    if err := nl.Wait(ctx); err != nil {\n        return\n    }\n    // Perform actions .........\n    nl.Finish()\n```\nIn the above example , there can be a maximum of 3 goroutines accessing a resource concurrently. The other goroutines are added to the waiting list and are removed and given a \nchance to access the resource in the FIFO order. If the context is cancelled , the goroutine is removed from the waitlist and `Wait` returns the context error.\n\n### Limiter with Timeout\n\n```go\n    nl := limiter.New(3,\n    WithTimeoutDuration(10 * time.Millisecond),\n    )\n    ctx := context.Background()\n    if err := nl.Wait(ctx); err != nil {\n        return\n    }\n    // Perform actions .........\n    nl.Finish()\n```\nIn the above example , the goroutines will wait for a maximum of 10 milliseconds. Goroutines will be removed from the waitlist after 10 ms and `Wait` will return `limiter.ErrTimeout`.\n\n### Limiter with Soft Timeout\n\n```go\n    nl := limiter.New(3,\n    WithTimeoutDuration(10 * time.Millisecond),\n    )\n    ctx := context.Background()\n    result, err := nl.WaitOrBypass(ctx)\n    if err != nil {\n        return\n    }\n    if result == limiter.AdmissionAcquired {\n        defer nl.Finish()\n    }\n    // Perform actions .........\n```\n\nIn the above example , the goroutine waits for up to 10 milliseconds to acquire real capacity. If capacity is still unavailable after the timeout, `WaitOrBypass` returns `limiter.AdmissionBypassed` and the caller can still proceed without consuming limiter capacity.\n\n### Priority Limiter\n\n```go\n    nl := priority.NewLimiter(3)\n    ctx := context.Background()\n    if err := nl.Wait(ctx , priority.High); err != nil {\n        return\n    }\n    // Perform actions .........\n    nl.Finish()\n```\n\nIn Priority Limiter , goroutines with higher priority will be given preference to be removed from the waitlist. For instance in the above example , the goroutine will be\ngiven the maximum preference because it is of high priority. In the case of tie between the priorities , the goroutines will be removed from the waitlist in the FIFO order.\n\n### Priority Limiter with Dynamic priority\n\n```go\n    nl := priority.NewLimiter(3,\n    WithDynamicPriorityDuration(5 * time.Millisecond),\n    )\n    ctx := context.Background()\n    if err := nl.Wait(ctx , priority.Low); err != nil {\n        return\n    }\n    // Perform actions .........\n    nl.Finish()\n```\nIn Dynamic Priority Limiter , the goroutines with lower priority will get their priority increased periodically by the time period specified. For instance in the above example , the goroutine will get it's priority increased every 5 ms. This will ensure that goroutines with lower priority do not suffer from starvation. It's highly recommended to use Dynamic Priority Limiter to avoid starving low priority goroutines.\n\n### Priority Limiter with Timeout\n\n```go\n    nl := priority.NewLimiter(3,\n    WithTimeoutDuration(30 * time.Millisecond),\n    WithDynamicPriorityDuration(5 * time.Millisecond),\n    )\n    ctx := context.Background()\n    if err := nl.Wait(ctx , priority.Low); err != nil {\n        return\n    }\n    // Perform actions .........\n    nl.Finish()\n```\nThis is similar to the timeouts in the normal limiter. In the above example , goroutines will wait a maximum of 30 milliseconds and `Wait` will return `limiter.ErrTimeout` if capacity is not acquired in time. The low priority goroutines will get their\npriority increased every 5 ms.\n\n### Priority Limiter with Soft Timeout\n\n```go\n    nl := priority.NewLimiter(3,\n    WithTimeoutDuration(30 * time.Millisecond),\n    WithDynamicPriorityDuration(5 * time.Millisecond),\n    )\n    ctx := context.Background()\n    result, err := nl.WaitOrBypass(ctx , priority.Low)\n    if err != nil {\n        return\n    }\n    if result == limiter.AdmissionAcquired {\n        defer nl.Finish()\n    }\n    // Perform actions .........\n```\n\nThis uses the same soft-admission model for the priority limiter. The goroutine waits up to the configured timeout while still participating in the priority queue. If capacity is not acquired in time, the call returns `limiter.AdmissionBypassed` and the caller may continue outside the limiter.\n\n### Runnable Function\n\n```go\n    nl := priority.NewLimiter(3)\n    ctx := context.Background()\n    nl.Run(ctx , priority.Low , func()error {\n        return sendMetrics()\n    })\n```\n\nRunnable function will allow you to wrap your function and execute them with concurrency limit. This function is a wrapper on top of the Wait() and Finish() functions.\nIf `Wait` fails because the context is cancelled or the timeout expires, `Run` returns that error and does not execute the callback.\n\n### Runnable Function with Soft Timeout\n\n```go\n    nl := priority.NewLimiter(3,\n    WithTimeoutDuration(30 * time.Millisecond),\n    )\n    ctx := context.Background()\n    result, err := nl.RunOrBypass(ctx , priority.Low , func()error {\n        return sendMetrics()\n    })\n    if err != nil {\n        return\n    }\n    _ = result\n```\n\n`RunOrBypass` executes the callback after either real admission or timeout bypass. It returns `limiter.AdmissionAcquired` when the limiter granted capacity and `limiter.AdmissionBypassed` when the callback ran outside the limiter after the timeout.\n\nThe older `WithTimeout(int)` and `WithDynamicPriority(int)` helpers are still supported for compatibility and continue to interpret their arguments as milliseconds.\n\n### Contribution\n\nPlease feel free to open up issues , create PRs for bugs/features. All contributions are welcome :)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvivek-ng%2Fconcurrency-limiter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvivek-ng%2Fconcurrency-limiter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvivek-ng%2Fconcurrency-limiter/lists"}