{"id":13413093,"url":"https://github.com/alitto/pond","last_synced_at":"2025-05-13T18:09:35.176Z","repository":{"id":37463335,"uuid":"248998145","full_name":"alitto/pond","owner":"alitto","description":"🔘 Minimalistic and High-performance goroutine worker pool written in Go","archived":false,"fork":false,"pushed_at":"2025-04-20T14:35:45.000Z","size":864,"stargazers_count":1749,"open_issues_count":1,"forks_count":71,"subscribers_count":27,"default_branch":"main","last_synced_at":"2025-04-25T15:48:43.574Z","etag":null,"topics":["concurrency","go","golang","golang-library","goroutine-pool","high-performance","pond","worker-pool","worker-pool-factory"],"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/alitto.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","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},"funding":{"github":"alitto"}},"created_at":"2020-03-21T14:56:33.000Z","updated_at":"2025-04-24T21:02:04.000Z","dependencies_parsed_at":"2024-07-10T14:25:59.956Z","dependency_job_id":"b7d92065-ed5f-40df-805d-fe669c4dd8fb","html_url":"https://github.com/alitto/pond","commit_stats":{"total_commits":62,"total_committers":9,"mean_commits":6.888888888888889,"dds":"0.22580645161290325","last_synced_commit":"f1f46f02a68fa34bb54c42f5311bee2ebbd30c89"},"previous_names":[],"tags_count":37,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alitto%2Fpond","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alitto%2Fpond/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alitto%2Fpond/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alitto%2Fpond/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alitto","download_url":"https://codeload.github.com/alitto/pond/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254000851,"owners_count":21997441,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["concurrency","go","golang","golang-library","goroutine-pool","high-performance","pond","worker-pool","worker-pool-factory"],"created_at":"2024-07-30T20:01:33.374Z","updated_at":"2025-05-13T18:09:35.153Z","avatar_url":"https://github.com/alitto.png","language":"Go","readme":"\u003ca title=\"Codecov\" target=\"_blank\" href=\"https://github.com/alitto/pond/actions\"\u003e\u003cimg alt=\"Build status\" src=\"https://github.com/alitto/pond/actions/workflows/main.yml/badge.svg?branch=main\u0026event=push\"/\u003e\u003c/a\u003e\n\u003ca title=\"Codecov\" target=\"_blank\" href=\"https://app.codecov.io/gh/alitto/pond/tree/main\"\u003e\u003cimg src=\"https://codecov.io/gh/alitto/pond/branch/main/graph/badge.svg\"/\u003e\u003c/a\u003e\n\u003ca title=\"Release\" target=\"_blank\" href=\"https://github.com/alitto/pond/releases\"\u003e\u003cimg src=\"https://img.shields.io/github/v/release/alitto/pond\"/\u003e\u003c/a\u003e\n\u003ca title=\"Go Report Card\" target=\"_blank\" href=\"https://goreportcard.com/report/github.com/alitto/pond/v2\"\u003e\u003cimg src=\"https://goreportcard.com/badge/github.com/alitto/pond/v2\"/\u003e\u003c/a\u003e\n\n\n\u003cimg src=\"./docs/logo.svg\" height=\"100\" /\u003e\n\npond is a minimalistic and high-performance Go library designed to elegantly manage concurrent tasks.\n\n## Motivation\n\nThis library is meant to provide a simple and idiomatic way to manage concurrency in Go programs. Based on the [Worker Pool pattern](https://en.wikipedia.org/wiki/Thread_pool), it allows running a large number of tasks concurrently while limiting the number of goroutines that are running at the same time. This is useful when you need to limit the number of concurrent operations to avoid resource exhaustion or hitting rate limits.\n\nSome common use cases include:\n - Processing a large number of tasks concurrently\n - Limiting the number of concurrent HTTP requests\n - Limiting the number of concurrent database connections\n - Sending HTTP requests to a rate-limited API\n\n## Features:\n\n- Zero dependencies\n- Create pools of goroutines that scale automatically based on the number of tasks submitted\n- Limit the number of concurrent tasks running at the same time\n- Worker goroutines are only created when needed and immediately removed when idle (scale to zero)\n- Minimalistic and fluent APIs for:\n  - Creating worker pools with maximum number of workers\n  - Submitting tasks to a pool and waiting for them to complete\n  - Submitting tasks to a pool in a fire-and-forget fashion\n  - Submitting a group of tasks and waiting for them to complete or the first error to occur\n  - Stopping a worker pool\n  - Monitoring pool metrics such as number of running workers, tasks waiting in the queue, etc.\n- Very high performance and efficient resource usage under heavy workloads, even outperforming unbounded goroutines in some scenarios\n- Complete pool metrics such as number of running workers, tasks waiting in the queue [and more](#metrics--monitoring)\n- Configurable parent context to stop all workers when it is cancelled\n- **New features in v2**:\n  - Bounded or Unbounded task queues\n  - Submission of tasks that return results\n  - Awaitable task completion\n  - Type safe APIs for tasks that return errors or results\n  - Panics recovery (panics are captured and returned as errors)\n  - Subpools with a fraction of the parent pool's maximum number of workers\n  - Blocking and non-blocking submission of tasks when the queue is full\n  - Dynamic resizing of the pool\n- [API reference](https://pkg.go.dev/github.com/alitto/pond/v2)\n\n## Installation\n\n```bash\ngo get -u github.com/alitto/pond/v2\n```\n\n## Usage\n\n### Submitting tasks to a pool with limited concurrency\n\n``` go\npackage main\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/alitto/pond/v2\"\n)\n\nfunc main() {\n\n\t// Create a pool with limited concurrency\n\tpool := pond.NewPool(100)\n\n\t// Submit 1000 tasks\n\tfor i := 0; i \u003c 1000; i++ {\n\t\ti := i\n\t\tpool.Submit(func() {\n\t\t\tfmt.Printf(\"Running task #%d\\n\", i)\n\t\t})\n\t}\n\n\t// Stop the pool and wait for all submitted tasks to complete\n\tpool.StopAndWait()\n}\n```\n\n### Submitting tasks that return errors\n\nThis feature allows you to submit tasks that return an `error`. This is useful when you need to handle errors that occur during the execution of a task.\n\n``` go\n// Create a pool with limited concurrency\npool := pond.NewPool(100)\n\n// Submit a task that returns an error\ntask := pool.SubmitErr(func() error {\n\treturn errors.New(\"An error occurred\")\n})\n\n// Wait for the task to complete and get the error\nerr := task.Wait()\n```\n\n### Submitting tasks that return results\n\nThis feature allows you to submit tasks that return a value. This is useful when you need to process the result of a task.\n\n``` go\n// Create a pool that accepts tasks that return a string and an error\npool := pond.NewResultPool[string](10)\n\n// Submit a task that returns a string\ntask := pool.Submit(func() (string) {\n\treturn \"Hello, World!\"\n})\n\n// Wait for the task to complete and get the result\nresult, err := task.Wait()\n// result = \"Hello, World!\" and err = nil\n```\n\n### Submitting tasks that return results or errors\n\nThis feature allows you to submit tasks that return a value and an `error`. This is useful when you need to handle errors that occur during the execution of a task.\n\n``` go\n// Create a concurrency limited pool that accepts tasks that return a string\npool := pond.NewResultPool[string](10)\n\n// Submit a task that returns a string value or an error\ntask := pool.SubmitErr(func() (string, error) {\n\treturn \"Hello, World!\", nil\n})\n\n// Wait for the task to complete and get the result\nresult, err := task.Wait()\n// result = \"Hello, World!\" and err = nil\n```\n\n### Submitting tasks associated with a context\n\nIf you need to submit a task that is associated with a context, you can pass the context directly to the task function.\n\n``` go\n// Create a pool with limited concurrency\npool := pond.NewPool(10)\n\n// Create a context that can be cancelled\nctx, cancel := context.WithCancel(context.Background())\n\n// Submit a task that is associated with a context\ntask := pool.SubmitErr(func() error {\n\treturn doSomethingWithCtx(ctx) // Pass the context to the task directly\n})\n\n// Wait for the task to complete and get the error.\n// If the context is cancelled, the task is stopped and an error is returned.\nerr := task.Wait()\n```\n\n### Submitting a group of related tasks\n\nYou can submit a group of tasks that are related to each other. This is useful when you need to execute a group of tasks concurrently and wait for all of them to complete.\n\n``` go\n// Create a pool with limited concurrency\npool := pond.NewPool(10)\n\n// Create a task group\ngroup := pool.NewGroup()\n\n// Submit a group of tasks\nfor i := 0; i \u003c 20; i++ {\n\ti := i\n\tgroup.Submit(func() {\n\t\tfmt.Printf(\"Running group task #%d\\n\", i)\n\t})\n}\n\n// Wait for all tasks in the group to complete\nerr := group.Wait()\n```\n\n### Submitting a group of related tasks associated with a context\n\nYou can submit a group of tasks that are linked to a context. This is useful when you need to execute a group of tasks concurrently and stop them when the context is cancelled (e.g. when the parent task is cancelled or times out).\n\n``` go\n// Create a pool with limited concurrency\npool := pond.NewPool(10)\n\n// Create a context with a 5s timeout\ntimeout, _ := context.WithTimeout(context.Background(), 5*time.Second)\n\n// Create a task group with a context\ngroup := pool.NewGroupContext(timeout)\n\n// Submit a group of tasks\nfor i := 0; i \u003c 20; i++ {\n\ti := i\n\tgroup.Submit(func() {\n\t\tfmt.Printf(\"Running group task #%d\\n\", i)\n\t})\n}\n\n// Wait for all tasks in the group to complete or the timeout to occur, whichever comes first\nerr := group.Wait()\n```\n\n### Submitting a group of related tasks and waiting for the first error\n\nYou can submit a group of tasks that are related to each other and wait for the first error to occur. This is useful when you need to execute a group of tasks concurrently and stop the execution if an error occurs.\n\n``` go\n// Create a pool with limited concurrency\npool := pond.NewPool(10)\n\n// Create a task group\ngroup := pool.NewGroup()\n\n// Submit a group of tasks\nfor i := 0; i \u003c 20; i++ {\n\ti := i\n\tgroup.SubmitErr(func() error {\n\t\tif n == 10 {\n\t\t\treturn errors.New(\"An error occurred\")\n\t\t}\n\t\tfmt.Printf(\"Running group task #%d\\n\", i)\n\t\treturn nil\n\t})\n}\n\n// Wait for all tasks in the group to complete or the first error to occur\nerr := group.Wait()\n```\n\n### Submitting a group of related tasks that return results\n\nYou can submit a group of tasks that are related to each other and return results. This is useful when you need to execute a group of tasks concurrently and process the results. Results are returned in the order they were submitted.\n\n``` go\n// Create a pool with limited concurrency\npool := pond.NewResultPool[string](10)\n\n// Create a task group\ngroup := pool.NewGroup()\n\n// Submit a group of tasks\nfor i := 0; i \u003c 20; i++ {\n\ti := i\n\tgroup.Submit(func() string {\n\t\treturn fmt.Sprintf(\"Task #%d\", i)\n\t})\n}\n\n// Wait for all tasks in the group to complete\nresults, err := group.Wait()\n// results = [\"Task #0\", \"Task #1\", ..., \"Task #19\"] and err = nil\n```\n\n### Stopping a group of tasks when a context is cancelled\n\nIf you need to submit a group of tasks that are associated with a context and stop them when the context is cancelled, you can pass the context directly to the task function.\n\n``` go\n// Create a pool with limited concurrency\npool := pond.NewPool(10)\n\n// Create a context that can be cancelled\nctx, cancel := context.WithCancel(context.Background())\n\n// Create a task group\ngroup := pool.NewGroupContext(ctx)\n\n// Submit a group of tasks\nfor i := 0; i \u003c 20; i++ {\n\ti := i\n\tgroup.SubmitErr(func() error {\n\t\treturn doSomethingWithCtx(ctx) // Pass the context to the task directly\n\t})\n}\n\n// Wait for all tasks in the group to complete.\n// If the context is cancelled, all tasks are stopped and the first error is returned.\nerr := group.Wait()\n```\n\n### Using a custom Context at the pool level\n\nEach pool is associated with a context that is used to stop all workers when the pool is stopped. By default, the context is the background context (`context.Background()`). You can create a custom context and pass it to the pool to stop all workers when the context is cancelled.\n\n```go\n// Create a custom context that can be cancelled\ncustomCtx, cancel := context.WithCancel(context.Background())\n\n// This creates a pool that is stopped when customCtx is cancelled \npool := pond.NewPool(10, pond.WithContext(customCtx))\n``` \n\n### Stopping a pool\n\nYou can stop a pool using the `Stop` method. This will stop all workers and prevent new tasks from being submitted. You can also wait for all submitted tasks by calling the `Wait` method.\n\n``` go\n// Create a pool with limited concurrency\npool := pond.NewPool(10)\n\n// Submit a task\npool.Submit(func() {\n\tfmt.Println(\"Running task\")\n})\n\n// Stop the pool and wait for all submitted tasks to complete\npool.Stop().Wait()\n```\n\nA shorthand method `StopAndWait` is also available to stop the pool and wait for all submitted tasks to complete.\n\n``` go\n// Create a pool with limited concurrency\npool := pond.NewPool(10)\n\n// Submit a task\npool.Submit(func() {\n\tfmt.Println(\"Running task\")\n})\n\n// Stop the pool and wait for all submitted tasks to complete\npool.StopAndWait()\n```\n\n### Recovering from panics\n\nBy default, panics that occur during the execution of a task are captured and returned as errors. This allows you to recover from panics and handle them gracefully.\n\n``` go\n// Create a pool with limited concurrency\npool := pond.NewPool(10)\n\n// Submit a task that panics\ntask := pool.Submit(func() {\n\tpanic(\"A panic occurred\")\n})\n\n// Wait for the task to complete and get the error\nerr := task.Wait()\n\nif err != nil {\n\tfmt.Printf(\"Failed to run task: %v\", err)\n} else {\n\tfmt.Println(\"Task completed successfully\")\n}\n```\n\n### Subpools (v2)\n\nSubpools are pools that can have a fraction of the parent pool's maximum number of workers. This is useful when you need to create a pool of workers that can be used for a specific task or group of tasks.\n\n``` go\n// Create a pool with limited concurrency\npool := pond.NewPool(10)\n\n// Create a subpool with a fraction of the parent pool's maximum number of workers\nsubpool := pool.NewSubpool(5)\n\n// Submit a task to the subpool\nsubpool.Submit(func() {\n\tfmt.Println(\"Running task in subpool\")\n})\n\n// Stop the subpool and wait for all submitted tasks to complete\nsubpool.StopAndWait()\n```\n\n### Default pool (v2)\n\nThe default pool is a global pool that is used when no pool is provided. This is useful when you need to submit tasks but don't want to create a pool explicitly.\nThe default pool does not have a maximum number of workers and scales automatically based on the number of tasks submitted.\n\n``` go\n// Submit a task to the default pool and wait for it to complete\nerr := pond.SubmitErr(func() error {\n\tfmt.Println(\"Running task in default pool\")\n\treturn nil\n}).Wait()\n\nif err != nil {\n\tfmt.Printf(\"Failed to run task: %v\", err)\n} else {\n\tfmt.Println(\"Task completed successfully\")\n}\n```\n\n### Bounded task queues (v2)\n\nBy default, task queues are unbounded, meaning that tasks are queued indefinitely until the pool is stopped (or the process runs out of memory). You can limit the number of tasks that can be queued by setting a queue size when creating a pool (`WithQueueSize` option).\n\n``` go\n// Create a pool with a maximum of 10 tasks in the queue\npool := pond.NewPool(1, pond.WithQueueSize(10))\n```\n\n**Blocking vs non-blocking task submission** \n\nWhen a pool defines a queue size (bounded), you can also specify how to handle tasks submitted when the queue is full. This can be done in two ways:\n- **Per task submission**: You can use `TrySubmit` and `TrySubmitErr` methods to attempt to submit a task and get a boolean indicating whether the task was submitted successfully.\n\n```go\n// Create a pool with a maximum of 10 tasks in the queue\npool := pond.NewPool(1, pond.WithQueueSize(10))\n\n// Submit a task to the pool\ntask, ok := pool.TrySubmit(func() {\n\t// Do some work\n})\n\n// Check if the task was submitted successfully\nif !ok {\n\tfmt.Println(\"Task submission failed because the queue is full\")\n}\n```\n\n- **Global non-blocking task submission**: You can set the `NonBlocking` option to `true` when creating a pool to enable non-blocking task submission. If the queue is full and non-blocking task submission is enabled, the task is dropped and an error is returned (`ErrQueueFull`).\n\n``` go\n// Create a pool with a maximum of 10 tasks in the queue and non-blocking task submission\npool := pond.NewPool(1, pond.WithQueueSize(10), pond.WithNonBlocking(true))\n```\n\n**Note**: you can technically use `TrySubmit` and `TrySubmitErr` methods or `NonBlocking` option for both bounded and unbounded task queues, but they are only useful when the queue is bounded. For unbounded task queues there is always space in the queue and non-blocking submission will always succeed.\n\n### Resizing pools (v2)\n\nYou can dynamically change the maximum number of workers in a pool using the `Resize` method. This is useful when you need to adjust the pool's capacity based on runtime conditions.\n\n``` go\n// Create a pool with 5 workers\npool := pond.NewPool(5)\n\n// Submit some tasks\nfor i := 0; i \u003c 20; i++ {\n    pool.Submit(func() {\n        // Do some work\n    })\n}\n\n// Increase the pool size to 10 workers\npool.Resize(10)\n\n// Submit more tasks that will use the increased capacity\nfor i := 0; i \u003c 20; i++ {\n    pool.Submit(func() {\n        // Do some work\n    })\n}\n\n// Decrease the pool size back to 5 workers\npool.Resize(5)\n```\n\nWhen resizing a pool:\n- The new maximum concurrency must be greater than or equal to 0 (0 means no limit)\n- If you increase the size, new workers will be created as needed up to the new maximum\n- If you decrease the size, existing workers will continue running until they complete their current tasks, but no new workers will be created until the number of running workers is below the new maximum\n\n### Metrics \u0026 monitoring\n\nEach worker pool instance exposes useful metrics that can be queried through the following methods:\n\n- `pool.RunningWorkers() int64`: Current number of running workers\n- `pool.SubmittedTasks() uint64`: Total number of tasks submitted since the pool was created and before it was stopped. This includes tasks that were dropped because the queue was full\n- `pool.WaitingTasks() uint64`: Current number of tasks in the queue that are waiting to be executed\n- `pool.SuccessfulTasks() uint64`: Total number of tasks that have successfully completed their execution since the pool was created\n- `pool.FailedTasks() uint64`: Total number of tasks that completed with panic since the pool was created\n- `pool.CompletedTasks() uint64`: Total number of tasks that have completed their execution either successfully or with panic since the pool was created\n- `pool.DroppedTasks() uint64`: Total number of tasks that were dropped because the queue was full since the pool was created\n\nIn our [Prometheus example](./examples/prometheus/main.go) we showcase how to configure collectors for these metrics and expose them to Prometheus.\n\n## Migrating from pond v1 to v2\n\nIf you are using pond v1, here are the changes you need to make to migrate to v2:\n\n1. Update the import path to `github.com/alitto/pond/v2`\n2. Replace `pond.New(100, 1000)` with `pond.NewPool(100)`. The second argument is no longer needed since task queues are unbounded by default.\n3. The pool option `pond.Context` was renamed to `pond.WithContext`\n4. The following pool options were deprecated:\n   - `pond.MinWorkers`: This option is no longer needed since workers are created on demand and removed when idle.\n   - `pond.IdleTimeout`: This option is no longer needed since workers are immediately removed when idle.\n   - `pond.PanicHandler`: Panics are captured and returned as errors. You can handle panics by checking the error returned by the `Wait` method.\n   - `pond.Strategy`: The pool now scales automatically based on the number of tasks submitted.\n5. The `pool.StopAndWaitFor` method was deprecated. Use `pool.Stop().Done()` channel if you need to wait for the pool to stop in a select statement.\n6. The `pool.Group` method was renamed to `pool.NewGroup`.\n7. The `pool.GroupContext` was renamed to `pool.NewGroupWithContext`.\n\n\n## Examples\n\nYou can find more examples in the [examples](./examples) directory.\n\n## API Reference\n\nFull API reference is available at https://pkg.go.dev/github.com/alitto/pond/v2\n\n## Benchmarks\n\nSee [Benchmarks](https://github.com/alitto/pond-benchmarks).\n\n## Resources\n\nHere are some of the resources which have served as inspiration when writing this library:\n\n- http://marcio.io/2015/07/handling-1-million-requests-per-minute-with-golang/\n- https://brandur.org/go-worker-pool\n- https://gobyexample.com/worker-pools\n- https://github.com/panjf2000/ants\n- https://github.com/gammazero/workerpool\n\n## Contribution \u0026 Support\n\nFeel free to send a pull request if you consider there's something that should be polished or improved. Also, please open up an issue if you run into a problem when using this library or just have a question about it.\n","funding_links":["https://github.com/sponsors/alitto"],"categories":["Goroutines","开源类库","Go","Relational Databases","Goroutines `goroutines的管理和使用`"],"sub_categories":["SQL 查询语句构建库","协程/线程","Advanced Console UIs","Search and Analytic Databases","检索及分析资料库"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falitto%2Fpond","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falitto%2Fpond","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falitto%2Fpond/lists"}