{"id":34514142,"url":"https://github.com/koykov/queue","last_synced_at":"2025-12-24T04:18:19.198Z","repository":{"id":52525850,"uuid":"313551960","full_name":"koykov/queue","owner":"koykov","description":"Common queue implementation with various features.","archived":false,"fork":false,"pushed_at":"2025-09-12T20:29:00.000Z","size":349,"stargazers_count":1,"open_issues_count":1,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-09-12T22:47:09.080Z","etag":null,"topics":["balance","deadline-aware","delayed-execution","leaky-bucket","leaky-buffer","priority","quality-of-service","queue","retryable","schedule","system-design","thread-pool"],"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/koykov.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":"license.md","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":"2020-11-17T08:18:48.000Z","updated_at":"2025-07-14T20:07:37.000Z","dependencies_parsed_at":"2023-07-12T16:11:54.586Z","dependency_job_id":"9baf6773-5d3d-49a0-a08a-1cbca4c04d75","html_url":"https://github.com/koykov/queue","commit_stats":null,"previous_names":["koykov/blqueue"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/koykov/queue","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/koykov%2Fqueue","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/koykov%2Fqueue/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/koykov%2Fqueue/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/koykov%2Fqueue/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/koykov","download_url":"https://codeload.github.com/koykov/queue/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/koykov%2Fqueue/sbom","scorecard":{"id":568743,"data":{"date":"2025-08-11","repo":{"name":"github.com/koykov/queue","commit":"8f93edaff8d22ab514e71e116fd2e7b76e85c45e"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4.3,"checks":[{"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/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Maintained","score":10,"reason":"18 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/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":0,"reason":"Found 0/4 approved changesets -- score normalized to 0","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/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"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/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"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/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"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/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: license.md:0","Info: FSF or OSI recognized license: MIT License: license.md:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"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/f6ed084d17c9236477efd66e5b258b9d4cc7b389/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/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"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/f6ed084d17c9236477efd66e5b258b9d4cc7b389/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/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-20T15:44:07.463Z","repository_id":52525850,"created_at":"2025-08-20T15:44:07.463Z","updated_at":"2025-08-20T15:44:07.463Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":27994568,"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","status":"online","status_checked_at":"2025-12-24T02:00:07.193Z","response_time":83,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["balance","deadline-aware","delayed-execution","leaky-bucket","leaky-buffer","priority","quality-of-service","queue","retryable","schedule","system-design","thread-pool"],"created_at":"2025-12-24T04:18:18.764Z","updated_at":"2025-12-24T04:18:19.191Z","avatar_url":"https://github.com/koykov.png","language":"Go","readme":"# Queue\n\nA `queue` is a wrapper over Go channels that has the following features:\n* balanced\n* leaky\n* retryable\n* backoff support\n* jitter support\n* scheduled\n* delayed execution\n* deadline-aware\n* prioretizable\n* covered with metrics\n* logged\n\n`queue` was developed in response to a need to create a lot of queues with the same structure and functionality. Create\nidentical channels with only different workers, cover them with metrics was too boring and as result this solution was born.\n\n`queue` allows to abstract from the implementation of the channel and producers and focus only on worker implementation.\nIt's enough to write a worker that implements [Worker](https://github.com/koykov/queue/blob/master/interface.go#L18),\nbind it to the [config](https://github.com/koykov/queue/blob/master/config.go#L22) of the queue, and it will do all work\nitself.\n\n`queue` isn't a classic queue with `Enqueue`/`Dequeue` methods. This implementation hasn't `Dequeue` method due to queue\nextracts items itself and forward them to any of active workers. That implementation is similar to\n[`thread pool`](https://en.wikipedia.org/wiki/Thread_pool) template, but `queue` go beyond frames of this template.\n\nQueue initializes using [`Config`](https://github.com/koykov/queue/blob/master/config.go#L22). It has only two necessary\nsettings - `Capacity` and `Worker`. `Worker` must implement [Worker](https://github.com/koykov/queue/blob/master/interface.go#L18)\ninterface. `Worker` can only process the item and return error if occurred. Workers count can be setup using `Workers`\nparam. If you want dynamic workers count, i.e. balanced queue, count should be setup using `WorkersMin`/`WorkersMax`\nparams (see balanced queue chapter).\n\nAs result queue will work as classic thread pool with static workers count.\n\nLet's see how enables and works other features of the `queue`.\n\n## Balanced queue\n\nUsually all queues has variadic load - maximum at the day, minimum at the night. The maximum number of workers must be\nactive independent of current load. It isn't a big problem due to goroutines is cheap, but solving the problem of \nbalancing count of workers depending on load was too interesting and therefore this feature was implemented.\n\nBalancing enables by setting up params `WorkersMin` and `WorkersMax` (`WorkersMin` must be less that `WorkersMax`).\nSetting up this params disables `Workers` param, i.e. balancing feature has high priority than static workers count.\n\n`WorkersMin` limits low count of active workers. Independent of conditions the queue will keep that number of workers\nactive.\n\n`WorkersMax` limits maximum count of active workers. Queue wouldn't run more workers than `WorkersMax`. Even if\n`WorkersMax` workers isn't enough, then leaky feature may help (see chapter Leaky queue).\n\nAll workers in range `WorkersMin` - `WorkersMax` may have three state:\n* _active_ - worker works and processes the items.\n* _sleep_ - worker is active, but does nothing due to queue hasn't enough items to process. This state isn't permanent -\nafter waiting `SleepInterval` worker become idle.\n* _idle_ - worker (goroutine) stops and release resources. By need queue may make idle worker to active (run goroutine).\n\nQueue makes a decision to run new worker when [rate](https://github.com/koykov/queue/blob/master/interface.go#L12)\nbecame greather than `WakeupFactor` [0..0.999999].\n\nEg: let's imagine queue with capacity 100 and `WakeupFactor` 0.5. If queue size will greater than 50, the queue will run\nnew worker. If new worker wouldn't help to reduce size, queue will start another one till rate become less than\n`WakeupFactor` or `WorkersMax` limit reached.\n\nLet's imagine next that queue's load reduces and count of active workers became redundant. In that case queue will check\n`SleepFactor` [0..0.999999]. If queue's rate become less that `SleepFactor` one of active workers will force to sleep\nstate. Next check another on will sleep, if condition (rate \u003c `SleepFactor`) keep true - till rate will greater that\n`SleepFactor` or `WorkersMin` limit reaches. Sleeping worker will not sleep forever. After waiting `SleepInterval`\nhis goroutine will stop and status become _idle_. Sleeping state is required for queues with often variadic load.\nPermanent goroutine running/stopping triggers `runtime.findrunnable` function. `SleepInterval` helps amortize that load. \n\nQueue in balancing mode permanent balances workers count so that queue's rate is between `SleepFactor` and `WakeupFactor`.\n\n## Leaky queue\n\nLet's imagine a queue with so huge load, that even `WorkersMax` active can't process the items in time. The queue blocks\nall threads calling `Enqueue`, that may produces deadlock or reduce performance.\n\nFor solving this problem was implemented `DLQ` (dead letter queue) - an auxiliary component, implements\n[Enqueuer](https://github.com/koykov/queue/blob/master/interface.go#L4) interface. Thus, you may forward leaked items to\nanother queue or even make a chain of queues.\n\nSetting up param `DLQ` in config enables \"leaky\" feature of the queue. It based on\n[\"leaky bucket algorithm\"](https://en.wikipedia.org/wiki/Leaky_bucket). It described in\n[Effective Go](https://golang.org/doc/effective_go#leaky_buffer) as \"leaky buffer\".\n\nPackage contains builtin [Dummy DLQ](https://github.com/koykov/queue/blob/master/dummy.go#L23) implementation. It just\nthrows leaked items to the trash - you will lose some items, but will keep queue and application alive. However, there are\n[dlqdump](https://github.com/koykov/dlqdump) solution, that may dump leaked items to some storage (eg: disk). See package\ndescription for details.\n\nFinal note of leaky queue: there is config flag `FailToDLQ`. If worker reports that item processing fails, the item will\nforward to `DLQ`, even if queue isn't leaked at the moment. It may be helpful for to make fallback method of item processing.\n\n## Retryable\n\nOne attempt of item processing may be not enough. For example, queue must send HTTP request and sending in worker fails\ndue to network problem and makes sense to try again. Param `MaxRetries` indicates how many repeated attempts worker can\ntake. The first attempt of processing isn't a retry. All next attempts interpreted as retry.\n\nThis param may work together with `FailToDLQ` param. Item will send to DLQ if all repeated attempts fails.\n\n## Backoff\n\nContinuation of the previous chapter.\n\nIf workers start throwing errors and mass retry is required, the queue may not be able to handle the load of both\nincoming elements and retryable elements. For this case, two parameters have been added:\n\n* `RetryInterval` - delay between processing attempts\n* `Backoff` - a special component that calculates the real delay time based on `RetryInterval`\n\nThe following backoffs are available out of the box:\n\n* [Linear](backoff/linear.go) (1, 2, 3, 4, ...)\n* [Exponential](backoff/exponential.go) (1, 2, 4, 8, ...)\n* [Quadratic](backoff/quadratic.go) (1, 4, 9, 16, ...)\n* [Polynomial](backoff/polynomial.go) (1, 8, 27, ...)\n* [Logarithmic](backoff/logarithmic.go) (for a delay of 10: 7, 11, 14, ...)\n* [Fibonacci](backoff/fibonacci.go) (1, 1, 2, 3, 5, 8, ...)\n* [Random](backoff/random.go) (`RetryInterval/2 + random(RetryInterval)`)\n\n## Jitter support\n\nContinuation of the previous point.\n\nIn case of mass retries, they can be synchronized in time and will go in waves, which will prevent the queue from processing them.\nEspecially for this case, there is a `Jitter` parameter, which allows you to \"smear\" retries in time by adding a random delay to the retry time.\n\nThe parameter only works with a non-zero `RetryInterval` and can work together with `Backoff`.\n\nThe following jitters are available out of the box:\n\n* [Full](jitter/full.go) - returns a random value in the interval [0..RetryInterval).\n* [Half](jitter/half.go) - returns a random value in the interval [RetryInterval/2 .. RetryInterval].\n* [Decorrelated](jitter/decorrelated.go) - a random value between [Min..Max] taking into account the previous value.\n\n## Scheduled queue\n\nLet's imagine we know periodicity of growing/reducing of queue load. For example, from 8:00 AM till 12:00 AM and from\n04:00 PM till 06:00 PM the load is moderate and 5 workers is enough. From 12:00 AM till 04:00 PM the load is maximal and\nminimum  10 workers must be active. And at night the load is lowes and one worker is enough. For that cases was implemented\n[scheduler](https://github.com/koykov/queue/blob/master/schedule.go) of queue params. It allows to set more optimal\nvalues of the following params for certain periods of time:\n* `WorkersMin`\n* `WorkersMax`\n* `WakeupFactor`\n* `SleepFactor`\n\nThese params replaces corresponding config's value in the given period.\n\nFor above example, the scheduler initialization look the following:\n```go\nsched := NewSchedule()\nsched.AddRange(\"08:00-12:00\", ScheduleParams{WorkersMin: 5, WorkersMax: 10})\nsched.AddRange(\"12:00-16:00\", ScheduleParams{WorkersMin: 10, WorkersMax: 20})\nsched.AddRange(\"16:00-18:00\", ScheduleParams{WorkersMin: 5, WorkersMax: 10})\nconfig := Config{\n\t...\n\tWorkersMin: 1,\n\tWorkersMax: 4,\n\tSchedule: sched,\n\t...\n}\n```\nThis config will balance queue for periods:\n* from 5 to 10 active workers in period 8:00 AM - 12:00 AM\n* from 10 to 20 active workers in period 12:00 AM - 04:00 PM\n* from 5 to 10 active workers in period 04:00 PM - 06:00 PM\n* from 1 to 4 active workers in the rest of time\n\nThe reason of this feature development is balances simplification in hot periods.  \n\n## Delayed execution queue (DEQ)\n\nIf queue must process item not immediately after enqueue, but after a period you may use param `DelayInterval`. Setting\nthis param enables DEQ feature and guarantees that item will process after at least `DelayInterval` period.\n\nThis param is opposite to `DeadlineInterval`.\n\n## Deadline-aware queue (DAQ)\n\nIn high-loaded queues the delivery of item to worker may take so much time that processing loss the meaning. The param\n`DeadlineInterval` may help in that case. If item acquires by worker, but `DeadlineInterval` (since enqueue) already\npassed, the item will not process.\n\nThis params may work together with `DeadlineToDLQ` flag.\n\nThis param is opposite to `DelayInterval`.\n\n## Prioretizable queue\n\nBy default, queue works as FIFO stack. It works good while queue gets items with the same priority. But if queue receives\nitems of both types - priority and non-priority sooner or later will happen the case when queue will have non-priority\nelements in the head and priority in the tail. Priority items may become outdated when they are turn and their processing\nwill lose maindness. In computer networks this problem was solved a long time ago and solution calls\n[Quality of Service (QoS)](https://en.wikipedia.org/wiki/Quality_of_service).\n\nThe config has param `QoS` with type [`qos.Config`](qos/config.go). Setting up this param makes the queue prioretizable.\nSee configuration details in [readme](qos/readme.md).\n\n## Metrics coverage\n\nConfig has a param calls `MetricsWriter` that must implement [`MetricsWriter`](https://github.com/koykov/queue/blob/master/metrics.go#L7)\ninterface.\n\nThere are two implementation of the interface:\n* [`log.MetricsWriter`](metrics/log)\n* [`prometheus.MetricsWriter`](metrics/prometheus)\n\nThe first is useless in production and may be need only for debugging purposes. Second one is totally tested and works\nwell. You may write your own implementation of `MetricsWriter` for any required TSDB.\n\n## Builtin workers\n\n`queue` has three helper workers:\n* [transit](https://github.com/koykov/queue/blob/master/worker/transit.go) just forwards the item to another queue.\n* [chain](https://github.com/koykov/queue/blob/master/worker/chain.go) joins several workers to one. The item will\nsynchronously processed by all \"child\" workers. You may, for example, build a chain of workers and finish it with\n`transit` worker.\n* [async_chain](https://github.com/koykov/queue/blob/master/worker/async_chain.go) also joins workers into one, but item will process asynchronously by \"child\" workers. \n\n## Logging\n\n`queue` may report about internal events (calibration(balancing), closing, worker signals, ...) for debugging purposes.\nThere is param `Logger` in config that must implement [`Logger`](https://github.com/koykov/queue/blob/master/logger.go)\ninterface.\n\n## Showcase\n\nDuring development the biggest problem was a covering with tests. Due to impossibility of unit-testing the \n[demo showcase](https://github.com/koykov/demo/tree/master/queue) project was developed, where were tested different\nscenarios of queue configs. The project has Docker-container, including `Grafana`, `Prometheus` and `queue daemon`.\nThe project controls using HTTP requests, see [readme](https://github.com/koykov/demo/blob/master/queue/readme.md). \n\nTypical sets of requests https://github.com/koykov/demo/tree/master/queue/request.\n\n## Links\n\n* https://en.wikipedia.org/wiki/Thread_pool\n* https://en.wikipedia.org/wiki/Leaky_bucket\n* https://golang.org/doc/effective_go#leaky_buffer\n* https://en.wikipedia.org/wiki/Dead_letter_queue\n* https://cloud.yandex.com/en/docs/message-queue/concepts/dlq\n* https://en.wikipedia.org/wiki/Quality_of_service\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkoykov%2Fqueue","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkoykov%2Fqueue","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkoykov%2Fqueue/lists"}