{"id":18683429,"url":"https://github.com/narasimha1997/ratelimiter","last_synced_at":"2025-08-07T14:33:26.122Z","repository":{"id":46625882,"uuid":"411593929","full_name":"Narasimha1997/ratelimiter","owner":"Narasimha1997","description":"A concurrent rate limiter library for Golang based on Sliding-Window rate limiter algorithm.","archived":false,"fork":false,"pushed_at":"2022-10-31T12:07:17.000Z","size":920,"stargazers_count":243,"open_issues_count":2,"forks_count":11,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-03-31T11:21:09.241Z","etag":null,"topics":["algorithm","concurrency","go","go-library","golang","gomodule","rate-limiting","ratelimiter"],"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/Narasimha1997.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":"2021-09-29T08:38:03.000Z","updated_at":"2025-03-07T00:02:12.000Z","dependencies_parsed_at":"2022-07-30T17:18:55.485Z","dependency_job_id":null,"html_url":"https://github.com/Narasimha1997/ratelimiter","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Narasimha1997%2Fratelimiter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Narasimha1997%2Fratelimiter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Narasimha1997%2Fratelimiter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Narasimha1997%2Fratelimiter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Narasimha1997","download_url":"https://codeload.github.com/Narasimha1997/ratelimiter/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252902676,"owners_count":21822276,"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":["algorithm","concurrency","go","go-library","golang","gomodule","rate-limiting","ratelimiter"],"created_at":"2024-11-07T10:14:38.271Z","updated_at":"2025-05-07T15:20:42.599Z","avatar_url":"https://github.com/Narasimha1997.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ratelimiter\n\n![Tests](https://github.com/Narasimha1997/ratelimiter/actions/workflows/test.yml/badge.svg)\n[![Go Reference](https://pkg.go.dev/badge/github.com/Narasimha1997/ratelimiter.svg)](https://pkg.go.dev/github.com/Narasimha1997/ratelimiter)\n\nA generic concurrent rate limiter library for Golang based on Sliding-window rate limitng algorithm.\n\nThe implementation of rate-limiter algorithm is based on Scalable Distributed Rate Limiter algorithm  used in Kong API gateway. Read [this blog](https://konghq.com/blog/how-to-design-a-scalable-rate-limiting-algorithm/) for more details.\n\nThis library can be used in your codebase to rate-limit literally anything. For example, you can integrate this library to provide rate-limiting for your REST/gRPC APIs or you can use this library to \nrate-limit the number of go-routines spawned or number of tasks submitted to a function/module per given time interval. This library provides generic rate check APIs that can be used anywhere. The library is built with concurrency in mind from the groud up, the rate-limiter can be used across go-routines without having to worry about synchronization issues. This library also provides capability to create and manage multiple rate-limiters with different configurations assiociated with unique keys.\n\n### How is this different from Go's official rate package?\nThe official Go [package](https://pkg.go.dev/golang.org/x/time/rate ) provides a rate limiter implementation which uses [Token Bucket Algorithm](https://en.wikipedia.org/wiki/Token_bucket). This repository (i.e the current repository) implements rate limiting functionalities using [Sliding Window Rate Limiting Algorithm](https://konghq.com/blog/how-to-design-a-scalable-rate-limiting-algorithm/) as used in Kong API gateway. Both of these libraries provide the same functionality, but there are trade-offs between these two algorithms, [this blog](https://konghq.com/blog/how-to-design-a-scalable-rate-limiting-algorithm/) properly explains these trade-offs. Understand the trade-offs and match them with your requirements to decide which algorithm to use.\n\n### Installation:\nThe package can be installed as a Go module.\n\n```\ngo get github.com/Narasimha1997/ratelimiter\n```\n\n### Using the library:\nThere are two types of rate-limiters used.\n\n#### All APIs:\n1. **Generic rate-limiter**:\n```go \n\t/* creates an instance of DefaultLimiter and returns it's pointer.\n\t   Parameters:\n\t \t\tlimit: The number of tasks to be allowd\n\t\t\tsize: duration\n\t*/\n\tfunc NewDefaultLimiter(limit uint64, size time.Duration) *DefaultLimiter\n\n\t/*\n\t\tKill the limiter, returns error if the limiter has been killed already.\n\t*/\n\tfunc (s *DefaultLimiter) Kill() error\n\n\t/*\n\t\tMakes decison whether n tasks can be allowed or not.\n\t\tParameters:\n\t\t\tn: number of tasks to be processed, set this as 1 for a single task. \n\t\t\t\t(Example: An HTTP request)\n\t\tReturns (bool, error),\n\t\t\tif limiter is inactive (or it is killed), returns an error\n\t\t\tthe boolean flag is either true - i.e n tasks can be allowed or false otherwise.\n\t*/\n\tfunc (s *DefaultLimiter) ShouldAllow(n uint64) (bool, error)\n\n\t/*\n\t\tKill the limiter, returns error if the limiter has been killed already.\n\t*/\n\tfunc (s *DefaultLimiter) Kill() error\t\n```\n\n2. **On-demand rate-limiter**\n```go\n\t/*  creates an instance of SyncLimiter and returns it's pointer.\n\t \tParameters:\n\t \t\tlimit: The number of tasks to be allowd\n\t\t\tsize: duration\n\t*/\n\tfunc NewSyncLimiter(limit uint64, size time.Duration) *SyncLimiter\n\n\t/*\n\t\tKill the limiter, returns error if the limiter has been killed already.\n\t*/\n\tfunc (s *SyncLimiter) Kill() error\n\n\t/*\n\t\tMakes decison whether n tasks can be allowed or not.\n\t\tParameters:\n\t\t\tn: number of tasks to be processed, set this as 1 for a single task. \n\t\t\t\t(Example: An HTTP request)\n\t\tReturns (bool, error),\n\t\t\tif limiter is inactive (or it is killed), returns an error\n\t\t\tthe boolean flag is either true - i.e n tasks can be allowed or false otherwise.\n\t*/\n\tfunc (s *SyncLimiter) ShouldAllow(n uint64) (bool, error)\n\n\t/*\n\t\tKill the limiter, returns error if the limiter has been killed already.\n\t*/\n\tfunc (s *SyncLimiter) Kill() error\n```\n\n3. **Attribute based Rate Limiter**\n```go\n\t/*\n\t\tCreates an instance of AttributeBasedLimiter and returns it's pointer.\n\t\tParameters:\n\t\t\tbackgroundSliding: if set to true, DefaultLimiter will be used as an underlying limiter.\n\t\t\t\t\t\t\t   else, SyncLimiter will be used.\n\t*/\n\tfunc NewAttributeBasedLimiter(backgroundSliding bool) *AttributeBasedLimiter\n\n\t/*\n\t\tCheck if AttributeBasedLimiter has a limiter for the key.\n\t\tParameters:\n\t\t\tkey: a unique key string, example: IP address, token, uuid etc\n\t\tReturns a boolean flag, if true, the key is already present, false otherwise.\n\t*/\n\tfunc (a *AttributeBasedLimiter) HasKey(key string) bool\n\n\t/*\n\t\tCreate a new key-limiter assiociation.\n\t\tParameters:\n\t\t\tkey: a unique key string, example: IP address, token, uuid etc\n\t\t\tlimit: The number of tasks to be allowd\n\t\t\tsize: duration\n\t\tReturns error if the key already exist.\n\t*/\n\n\tfunc (a *AttributeBasedLimiter) CreateNewKey(\n\t\tkey string, limit uint64, \n\t\tsize time.Duration,\n\t) error\n\n\t/* \n\t   check if AttributeBasedLimiter has a limiter for the key.\n\t   Create a new key-limiter assiociation if the key not exists.\n\t   Parameters:\n\t    key: a unique key string, example: IP address, token, uuid etc.\n\t\tlimit: The number of tasks to be allowd\n\t\tsize: duration\n\t\tReturn true if the key exists or is created successfully.\n\t*/\n\tfunc (a *AttributeBasedLimiter) HasOrCreateKey(key string, limit uint64, size time.Duration);\n\n\t/*\n\t\tMakes decison whether n tasks can be allowed or not.\n\t\tParameters:\n\t\t\tkey: a unique key string, example: IP address, token, uuid etc\n\t\t\tn: number of tasks to be processed, set this as 1 for a single task. \n\t\t\t\t(Example: An HTTP request)\n\t\tReturns (bool, error),\n\t\t\tif limiter is inactive (or it is killed) or key is not present, returns an error\n\t\t\tthe boolean flag is either true - i.e n tasks can be allowed or false otherwise.\n\t*/\n\tfunc (a *AttributeBasedLimiter) ShouldAllow(key string, n uint64) (bool, error)\n\n\t/* \n\t\tMustShouldAllow makes decison whether n tasks can be allowed or not.\n\t\tCreates a new key if it does not exist.\n\t\tParameters:\n\t\t\tkey: a unique key string, example: IP address, token, uuid etc\n\t\t\tn: number of tasks to be processed, set this as 1 for a single task.\n\t\t\t(Example: An HTTP request)\n\t\t\tlimit: The number of tasks to be allowd\n\t\t\tsize: duration\n\n\t\tReturns bool.\n\t\t\t(false) when limiter is inactive (or it is killed) or n tasks can be not allowed.\n\t\t\t(true) when n tasks can be allowed or new key-limiter.\n\t*/\n\tfunc (a *AttributeBasedLimiter) MustShouldAllow(key string, n uint64, limit uint64, size time.Duration) bool\n\n\t/*\n\t\tRemove the key and kill its underlying limiter.\n\t\tParameters:\n\t\t\tkey: a unique key string, example: IP address, token, uuid etc\n\t\tReturns an error if the key is not present.\n\t*/\n\tfunc (a *AttributeBasedLimiter) DeleteKey(key string) error\n```\n\n### Examples and Explanation of each type of rate-limiter:\n#### Generic rate-limiter\nThe generic rate-limiter instance can be created if you want to have a single rate-limiter with single configuration for everything. The generic rate-limiter can be created by calling `NewDefaultLimiter()` function and by passing the `limit` and `size` as parameters. Example:\n\n```go\nfunc GenericRateLimiter() {\n\t/* create an instance of Limiter.\n\tformat: NewLimiter(limit uint64, size time.Duration),\n\twhere:\n\t\tlimit: The number of tasks/items that should be allowed.\n\t\tsize: The window size, i.e the time interval during which the limit\n\t\t\t\tshould be imposed.\n\t\tTo summarize, if limit = 100 and duration = 5s, then allow 100 items per 5 seconds\n\t*/\n\n\tlimiter := ratelimiter.NewDefaultLimiter(\n\t\t100, time.Second*5,\n\t)\n\n\t/*\n\t\tCleaning up the limiter: Once the limiter is no longer required,\n\t\tthe underlying goroutines and resources used by the limiter can be cleaned up.\n\t\tThis can be done using:\n\t\t\tlimiter.Kill(),\n\t\tReturns an error if the limiter is already being killed.\n\t*/\n\n\tdefer limiter.Kill()\n\n\t/*\n\t\tthe limiter provides ShouldAllow(N uint64) function which\n\t\treturns true/false if N items/tasks can be allowed during current\n\t\ttime interval.\n\n\t\tAn error is returned if the limiter is already killed.\n\t*/\n\n\t// ShouldAllow(N uint64) -\u003e returns bool, error\n\n\t// should return true\n\tfmt.Println(limiter.ShouldAllow(60))\n\t// should return false, because (60 + 50 = 110) \u003e 100 during this window\n\tfmt.Println(limiter.ShouldAllow(50))\n\t// sleep for some time\n\ttime.Sleep(5 * time.Second)\n\t// should return true, because the previous window has been slided over\n\tfmt.Println(limiter.ShouldAllow(20))\n}\n```\n\n#### On demand window sliding:\nThe previous method i.e the Generic Rate limiter spins up a background goroutine that takes care of sliding the rate-limiting window whenever it's size expires, because of this, rate-limiting check function `ShouldAllow` has fewer steps and takes very less time to make decision. But if your application manages a large number of Limiters, for example a web-server that performs rate-limiting across hundreds of different IPs, then your `AttributeBasedRateLimiter` spins up a goroutine for each unique IP and thus lot of such routines needs to be manitanied, this might induce scheduling pressure.\n\nAn alternative solution is to use a rate-limiter does not require a background routine, instead the window is sliding is taken care by `ShouldAllow` function itself, this method can be used to maintain large number of rate limiters without any scheduling pressure. This limiter is called `SyncLimiter` and can be used just like `DefaultLimiter`, because `SyncLimiter` and `DefaultLimiter` are built on top of the same `Limiter` interface. To use this, just replace `NewDefaultLimiter` with `NewSyncLimiter`\n```go\n......\n\n\tlimiter := ratelimiter.NewSyncLimiter(\n\t\t100, time.Second*5,\n\t)\n......\n```\n\n#### Attribute based rate-limiter:\nAttribute based rate-limiter can hold multiple rate-limiters with different configurations in a map\nof \u003cstring, Limiter\u003e type. Each limiter is uniquely identified by a key. Calling  `NewAttributeBasedLimiter()` will create an empty rate limiter with no entries.\n\n```go\nfunc AttributeRateLimiter() {\n\t/*\n\t\tAttribute based rate-limiter can hold multiple\n\t\trate-limiters with different configurations in a map\n\t\tof \u003cstring, Limiter\u003e type. Each limiter is uniquely identified\n\t\tby a key. Calling NewAttributeBasedLimiter() will create an empty\n\t\trate limiter with no entries.\n\t*/\n\t/*\n\t\tAttribute based rate-limiter has a boolean parameter called:\n\t\t`backgroundSliding` - if set to true, the attribute based rate-limiter\n\t\tuses Limiter instance and each Limiter instance have it's own background goroutine\n\t\tto manage sliding window. This might be resource expensive for large number of attributes,\n\t\tbut is faster than SyncLimiter.\n\n\t\tDisable this, i.e pass `false` if you want to manage large number of attributes\n\t\tin less memory and compute, sacrifcing a minimal amount of performance.\n\t*/\n\tlimiter := ratelimiter.NewAttributeBasedLimiter(true)\n\n\t/*\n\t\tNow we are adding a new entry to the limiter, we pass:\n\t\t\tkey: A string that is used to uniquely identify the rate-limiter.\n\t\t\tlimit: The number of tasks/items that should be allowed.\n\t\t\tsize: The window size, i.e the time interval during which the limit\n\t\t\t\tshould be imposed.\n\n\t\treturns error if the key already exists in the map.\n\t*/\n\t// we have two articles here (for example)\n\tarticle_ids := []string{\"article_id=10\", \"article_id=11\"}\n\n\t// for article_id=10, allow 10 tasks/items per every second\n\terr := limiter.CreateNewKey(\u0026article_ids[0], 10, 5*time.Second)\n\tif err != nil {\n\t\tlog.Fatalln(err)\n\t}\n\t// for article_id=11, allow 100 tasks/items per every 6 minutes\n\terr = limiter.CreateNewKey(\u0026article_ids[1], 100, 6*time.Minute)\n\tif err != nil {\n\t\tlog.Fatalln(err)\n\t}\n\t// rates can be checked by passing key and N as parameters\n\t// Can I make 8 requests to article_id=10 during this time window?\n\n\t// ShouldAllow(key *string, N uint64) returns (bool, error)\n\t// the bool is true/false, true if it can be allowed\n\t// false if it cant be allowed.\n\t// error if key is not found.\n\n\tfmt.Println(limiter.ShouldAllow(\u0026article_ids[0], 8))\n\t// Can I make 104 requests to article_id=11 during this time window?\n\tfmt.Println(limiter.ShouldAllow(\u0026article_ids[0], 104))\n\n\t/*\n\t\tOther functions:\n\t\t\t1. HasKey: to check if the attribute already has given key\n\t\t\t   call: HasKey(key string) function.\n\t\t\t   Example: limiter.HasKey(\u0026article_id[0])\n\t\t\t   Returns a bool, true if exists, false otherwise\n\n\t\t\t2. DeleteKey: to remove the key from attribute map\n\t\t\t   call: DeleteKey(key string) function.\n\t\t\t   Example: limiter.DeleteKey(\u0026article_id[1])\n\t\t\t   Returns an error, if key was not in the map.\n\t*/\n}\n```\n\n### Using ratelimiter as a middleware with HTTP web server:\nratelimiter is pluggable and can be used anywhere. This code snippet shows how it can be used with\nGo's standard HTTP library when building a web server:\n\n```go\n.....\n// allow 100 requests every 5 seconds\nlimiter := ratelimiter.NewSyncLimiter(100, time.Second * 5)\n\n// register the handler\nrateLimiterHandler := func(next http.Handler) http.Handler {\n\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tallowed, err := limiter.ShouldAllow(1)\n\t\tif err != nil {\n\t\t\tlog.Fatalln(err)\n\t\t}\n\t\tif allowed {\n\t\t\tnext.ServeHTTP(w, r)\n\t\t}\n\t})\n}\n\n// create a test route handler\nponger := func(w http.ResponseWriter, r *http.Request) {\n\tw.Write([]byte(\"Pong!!\"))\n}\n\n// attach the ratelimiter middleware:\nmuxServer := http.NewServeMux()\nmuxServer.Handle(\"/\", rateLimiterHandler(\n\thttp.HandlerFunc(ponger),\n))\n\n// start the server\nerr := http.ListenAndServe(\":6000\", muxServer)\nif err != nil {\n\tlog.Fatalln(err)\n}\n```\nThe complete example can be found at `examples/http-server/server.go`.\n`curl` was used to simulate `X` requests per second and following was the output, as logged.\n```\n................................\n2021/10/05 14:12:38 Iteration: 7, Requests received: 522, Allowed: 99\n2021/10/05 14:12:43 Iteration: 8, Requests received: 533, Allowed: 101\n2021/10/05 14:12:48 Iteration: 9, Requests received: 515, Allowed: 100\n2021/10/05 14:12:53 Iteration: 10, Requests received: 505, Allowed: 100\n2021/10/05 14:12:58 Iteration: 11, Requests received: 508, Allowed: 100\n2021/10/05 14:13:03 Iteration: 12, Requests received: 474, Allowed: 100\n2021/10/05 14:13:08 Iteration: 13, Requests received: 495, Allowed: 100\n2021/10/05 14:13:13 Iteration: 14, Requests received: 478, Allowed: 100\n..................................\n```\nThe ratelimiter was able to balance the requested limit as specified.\nIf you have installed the package, you can simply run the webserver as follows:\n```\ngo run examples/http-server/server.go\n```\n\n### Testing\nTests are written in `attribute_limiter_test.go` and `limiter_test.go` files. To execute the tests, \nsimply run:\n```\ngo test ./ -v\n```\n\nThese are some of the results from tests:\n1. **Single goroutine, Generic limiter**: This test configures the rate-limiter to allow 100 requests/sec and fires 500 requests/sec with a time gap of 2ms each, allowed requests are counted and is tested with difference +/- 3. The same test is run for 10 samples. Here are the results:\n\n```\n=== RUN   TestLimiterAccuracy\nIteration 1, Allowed tasks: 100, passed rate limiting accuracy test.\nIteration 2, Allowed tasks: 101, passed rate limiting accuracy test.\nIteration 3, Allowed tasks: 100, passed rate limiting accuracy test.\nIteration 4, Allowed tasks: 100, passed rate limiting accuracy test.\nIteration 5, Allowed tasks: 100, passed rate limiting accuracy test.\nIteration 6, Allowed tasks: 100, passed rate limiting accuracy test.\nIteration 7, Allowed tasks: 101, passed rate limiting accuracy test.\nIteration 8, Allowed tasks: 100, passed rate limiting accuracy test.\nIteration 9, Allowed tasks: 100, passed rate limiting accuracy test.\nIteration 10, Allowed tasks: 100, passed rate limiting accuracy test.\n--- PASS: TestLimiterAccuracy (10.01s)\n```\n\n2. **4 goroutines, Generic Limiter**: This test configures the limiter to allow 100 requests/sec and spins up 4 goroutines, the same limiter is shared across all the routines. Each goroutine generates 500 requests/sec with 2ms time gap between 2 requests. Allowed requests are counted per each goroutine, the result sum of all counts should be almost equal to 100. The accuracy is measured considering +/- 3 as error offset. The same test is conducted 10 times. Here are the results:\n\n```\n=== RUN   TestConcurrentLimiterAccuracy\nIteration 1, Allowed tasks: 101, passed rate limiting accuracy test.\nIteration 2, Allowed tasks: 100, passed rate limiting accuracy test.\nIteration 3, Allowed tasks: 100, passed rate limiting accuracy test.\nIteration 4, Allowed tasks: 100, passed rate limiting accuracy test.\nIteration 5, Allowed tasks: 100, passed rate limiting accuracy test.\nIteration 6, Allowed tasks: 100, passed rate limiting accuracy test.\nIteration 7, Allowed tasks: 100, passed rate limiting accuracy test.\nIteration 8, Allowed tasks: 100, passed rate limiting accuracy test.\nIteration 9, Allowed tasks: 100, passed rate limiting accuracy test.\nIteration 10, Allowed tasks: 100, passed rate limiting accuracy test.\n--- PASS: TestConcurrentLimiterAccuracy (10.01s)\n```\n\n3. **2 goroutines, 2 attribute keys, Attribute based limiter**: An attribute based limiter is created with 2 keys, these keys are configured to allow 100 requests/sec and 123 requests/sec respectively. Two goroutines are created and same attribute based limiter is shared across. Each goroutine produces 500 requests/sec per key. The overall count is then verified for each goroutine with error offset of +/- 3. Here are the results:\n\n```\n=== RUN   TestAttributeBasedLimiterAccuracy\nIteration 1, Allowed tasks: 100, passed rate limiting accuracy test.\nIteration 1, Allowed tasks: 123, passed rate limiting accuracy test.\nIteration 2, Allowed tasks: 101, passed rate limiting accuracy test.\nIteration 2, Allowed tasks: 124, passed rate limiting accuracy test.\nIteration 3, Allowed tasks: 100, passed rate limiting accuracy test.\nIteration 3, Allowed tasks: 123, passed rate limiting accuracy test.\nIteration 4, Allowed tasks: 100, passed rate limiting accuracy test.\nIteration 4, Allowed tasks: 123, passed rate limiting accuracy test.\nIteration 5, Allowed tasks: 100, passed rate limiting accuracy test.\nIteration 5, Allowed tasks: 123, passed rate limiting accuracy test.\n--- PASS: TestAttributeBasedLimiterAccuracy (5.00s)\n```\n\n**Code coverage**:\nTo generate code coverage report, execute:\n```\ngo test -coverprofile=c.out\n```\n\nThis should print the following after running all the tests.\n```\ncoverage: 99.0% of statements\nok      github.com/Narasimha1997/ratelimiter    25.099s\n```\n\nYou can also save the results as HTML for more detailed code view of the coverage.\n```\ngo tool cover -html=c.out -o coverage.html\n```\n\nThis will generate a file called `coverage.html`. The `coverage.html` is provided in the repo which is pre-generated.\n\n**Benchmarks**:\nBenchmarks can be executed by running:\n```\ngo test -bench=.\n```\n\nCurrent benchmarks are as follows:\n```\ngoos: linux\ngoarch: amd64\npkg: github.com/Narasimha1997/ratelimiter\ncpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz\nBenchmarkDefaultLimiter-12                      11732958                85.61 ns/op\nBenchmarkSyncLimiter-12                          7047988               175.9 ns/op\nBenchmarkConcurrentDefaultLimiter-12             7017625               163.9 ns/op\nBenchmarkConcurrentSyncLimiter-12                4132976               256.3 ns/op\nPASS\nok      github.com/Narasimha1997/ratelimiter    46.408s\n```\n\n#### Notes on test:\nThe testing code produces 500 requests/sec with `2ms` precision time gap between each request. The accuracy of this `2ms` time tick generation can differ from platform to platform, even a small difference of 500 micorseconds can add up together and give more time for test to run in the end because of clock drift, as a result the error offset +/- 3 might not always work.\n### Contributing\nFeel free to raise issues, make pull requests or suggest new features.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnarasimha1997%2Fratelimiter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnarasimha1997%2Fratelimiter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnarasimha1997%2Fratelimiter/lists"}