{"id":13564072,"url":"https://github.com/mailgun/gubernator","last_synced_at":"2025-10-05T11:31:54.606Z","repository":{"id":37873683,"uuid":"167429490","full_name":"mailgun/gubernator","owner":"mailgun","description":"High Performance Rate Limiting MicroService and Library","archived":true,"fork":false,"pushed_at":"2024-04-19T14:07:07.000Z","size":3525,"stargazers_count":964,"open_issues_count":16,"forks_count":99,"subscribers_count":66,"default_branch":"master","last_synced_at":"2024-11-15T07:33:12.796Z","etag":null,"topics":["cloudnative","golang","golang-library","grpc","microservice","rate-limiter","rate-limiting"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mailgun.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":"CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-01-24T20:05:55.000Z","updated_at":"2024-09-10T14:58:45.000Z","dependencies_parsed_at":"2023-02-09T13:15:57.660Z","dependency_job_id":"8ccd2cc5-48ac-4961-a032-ccb0f71070ce","html_url":"https://github.com/mailgun/gubernator","commit_stats":{"total_commits":488,"total_committers":42,"mean_commits":"11.619047619047619","dds":0.6045081967213115,"last_synced_commit":"d3b62bb48d2d8fbd30c77dd654b84fcadc69a222"},"previous_names":[],"tags_count":89,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mailgun%2Fgubernator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mailgun%2Fgubernator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mailgun%2Fgubernator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mailgun%2Fgubernator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mailgun","download_url":"https://codeload.github.com/mailgun/gubernator/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":235393591,"owners_count":18982818,"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":["cloudnative","golang","golang-library","grpc","microservice","rate-limiter","rate-limiting"],"created_at":"2024-08-01T13:01:26.268Z","updated_at":"2025-10-05T11:31:54.236Z","avatar_url":"https://github.com/mailgun.png","language":"Go","funding_links":[],"categories":["Authorization","Go"],"sub_categories":["Open-source policy frameworks"],"readme":"\u003ch2 align=\"center\"\u003e\n    \u003cimg src=\"contrib/assets/gubernator-logo.png\" alt=\"Gubernator Logo\" width=\"336px\" /\u003e\u003cbr /\u003e\n    Distributed RateLimiting Service\n\u003c/h2\u003e\n\n\u003e ## DEVELOPMENT ON GUBERNATOR HAS MOVED TO A NEW HOME AT [gubernator-io/gubernator](https://github.com/gubernator-io/gubernator)\n\u003e v2.4.0 is the final version available from his repo, all new features and bug fixes will occur under the new repo.\n\n# Gubernator\nGubernator is a distributed, high performance, cloud native and stateless rate-limiting service.\n\n#### Features\n* Gubernator evenly distributes rate limit requests across the entire cluster,\n  which means you can scale the system by simply adding more nodes. \n* Gubernator doesn’t rely on external caches like memcached or redis, as such\n  there is no deployment synchronization with a dependant service. This makes\n  dynamically growing or shrinking the cluster in an orchestration system like\n  kubernetes or nomad trivial.\n* Gubernator holds no state on disk, It’s configuration is passed to it by the\n  client on a per-request basis.\n* Gubernator provides both GRPC and HTTP access to the API.\n* It Can be run as a sidecar to services that need rate limiting or as a separate service.\n* It Can be used as a library to implement a domain-specific rate limiting service.\n* Supports optional eventually consistent rate limit distribution for extremely\n  high throughput environments. (See GLOBAL behavior [architecture.md](docs/architecture.md))\n* Gubernator is the english pronunciation of governor in Russian, also it sounds cool.\n\n### Stateless configuration\nGubernator is stateless in that it doesn’t require disk space to operate. No\nconfiguration or cache data is ever synced to disk. This is because every\nrequest to gubernator includes the config for the rate limit. At first you\nmight think this an unnecessary overhead to each request. However, In reality a\nrate limit config is made up of only 4, 64bit integers.\n\n### Quick Start\n```bash\n# Download the docker-compose file\n$ curl -O https://raw.githubusercontent.com/mailgun/gubernator/master/docker-compose.yaml\n# Run the docker container\n$ docker-compose up -d\n```\nNow you can make rate limit requests via CURL\n```\n# Hit the HTTP API at localhost:9080 (GRPC is at 9081)\n$ curl http://localhost:9080/v1/HealthCheck\n\n# Make a rate limit request\n$ curl http://localhost:9080/v1/GetRateLimits \\\n  --header 'Content-Type: application/json' \\\n  --data '{\n    \"requests\": [\n        {\n            \"name\": \"requests_per_sec\",\n            \"uniqueKey\": \"account:12345\",\n            \"hits\": \"1\",\n            \"limit\": \"10\",\n            \"duration\": \"1000\"\n        }\n    ]\n}'\n```\n\n### ProtoBuf Structure\n\nAn example rate limit request sent via GRPC might look like the following\n```yaml\nrate_limits:\n    # Scopes the request to a specific rate limit\n  - name: requests_per_sec\n    # A unique_key that identifies this instance of a rate limit request\n    unique_key: account_id=123|source_ip=172.0.0.1\n    # The number of hits we are requesting\n    hits: 1\n    # The total number of requests allowed for this rate limit\n    limit: 100\n    # The duration of the rate limit in milliseconds\n    duration: 1000\n    # The algorithm used to calculate the rate limit\n    # 0 = Token Bucket\n    # 1 = Leaky Bucket\n    algorithm: 0\n    # The behavior of the rate limit in gubernator.\n    # 0 = BATCHING (Enables batching of requests to peers)\n    # 1 = NO_BATCHING (Disables batching)\n    # 2 = GLOBAL (Enable global caching for this rate limit)\n    behavior: 0\n```\n\nAn example response would be\n\n```yaml\nrate_limits:\n    # The status of the rate limit.  OK = 0, OVER_LIMIT = 1\n  - status: 0,\n    # The current configured limit\n    limit: 10,\n    # The number of requests remaining\n    remaining: 7,\n    # A unix timestamp in milliseconds of when the bucket will reset, or if \n    # OVER_LIMIT is set it is the time at which the rate limit will no \n    # longer return OVER_LIMIT.\n    reset_time: 1551309219226,\n    # Additional metadata about the request the client might find useful\n    metadata:\n      # This is the name of the coordinator that rate limited this request\n      \"owner\": \"api-n03.staging.us-east-1.mailgun.org:9041\"\n```\n\n### Rate limit Algorithm\nGubernator currently supports 2 rate limit algorithms.\n\n1. **Token Bucket** implementation starts with an empty bucket, then each `Hit`\n   adds a token to the bucket until the bucket is full. Once the bucket is\n   full, requests will return `OVER_LIMIT` until the `reset_time` is reached at\n   which point the bucket is emptied and requests will return `UNDER_LIMIT`.\n   This algorithm is useful for enforcing very bursty limits. (IE: Applications\n   where a single request can add more than 1 `hit` to the bucket; or non network\n   based queuing systems.) The downside to this implementation is that once you\n   have hit the limit no more requests are allowed until the configured rate\n   limit duration resets the bucket to zero.\n\n2. [Leaky Bucket](https://en.wikipedia.org/wiki/Leaky_bucket) is implemented\n   similarly to **Token Bucket** where `OVER_LIMIT` is returned when the bucket\n   is full. However tokens leak from the bucket at a consistent rate which is\n   calculated as `duration / limit`. This algorithm is useful for metering, as\n   the bucket leaks allowing traffic to continue without the need to wait for\n   the configured rate limit duration to reset the bucket to zero.\n\n### Performance\nIn our production environment, for every request to our API we send 2 rate\nlimit requests to gubernator for rate limit evaluation, one to rate the HTTP\nrequest and the other is to rate the number of recipients a user can send an\nemail too within the specific duration. Under this setup a single gubernator\nnode fields over 2,000 requests a second with most batched responses returned\nin under 1 millisecond.\n\n![requests graph](contrib/assets/requests-graph.png)\n\nPeer requests forwarded to owning nodes typically respond in under 30 microseconds. \n\n![peer requests graph](contrib/assets/peer-requests-graph.png)\n\nNOTE The above graphs only report the slowest request within the 1 second sample time.\n So you are seeing the slowest requests that gubernator fields to clients.\n\nGubernator allows users to choose non-batching behavior which would further\nreduce latency for client rate limit requests. However because of throughput\nrequirements our production environment uses Behaviour=BATCHING with the\ndefault 500 microsecond window. In production we have observed batch sizes of\n1,000 during peak API usage. Other users who don’t have the same high traffic\ndemands could disable batching and would see lower latencies but at the cost of\nthroughput.\n\n## Gregorian Behavior\nUsers may choose a behavior called `DURATION_IS_GREGORIAN` which changes the \nbehavior of the `Duration` field. When `Behavior` is set to `DURATION_IS_GREGORIAN` \nthe `Duration` of the rate limit is reset whenever the end of selected gregorian \ncalendar interval is reached.\n\nThis is useful when you want to impose daily or monthly limits on a resource. Using\nthis behavior you know when the end of the day or month is reached the limit on the\nresource is reset regardless of when the first rate limit request was received by\nGubernator.\n\nGiven the following `Duration` values\n*  0 = Minutes\n*  1 = Hours\n*  2 = Days\n*  3 = Weeks\n*  4 = Months\n*  5 = Years\n\nExamples when using `Behavior = DURATION_IS_GREGORIAN`\n* If  `Duration = 2` (Days) then the rate limit will reset to `Current = 0` at the end of the current day the rate limit was created.\n* If `Duration = 0` (Minutes) then the rate limit will reset to `Current = 0` at the end of the minute the rate limit was created.\n* If `Duration = 4` (Months) then the rate limit will reset to `Current = 0` at the end of the month the rate limit was created.\n\n## Reset Remaining Behavior\nUsers may add behavior `Behavior_RESET_REMAINING` to the rate check request.\nThis will reset the rate limit as if created new on first use.\n\nWhen using Reset Remaining, the `Hits` field should be 0.\n\n## Drain Over Limit Behavior\nUsers may add behavior `Behavior_DRAIN_OVER_LIMIT` to the rate check request.\nA `GetRateLimits` call drains the remaining counter on first over limit event.\nThen, successive `GetRateLimits` calls will return zero remaining counter and\nnot any residual value.  This behavior works best with token bucket algorithm\nbecause the `Remaining` counter will stay zero after an over limit until reset\ntime, whereas leaky bucket algorithm will immediately update `Remaining` to a\nnon-zero value.\n\nThis facilitates scenarios that require an over limit event to stay over limit\nuntil the rate limit resets.  This approach is necessary if a process must make\ntwo rate checks, before and after a process, and the `Hit` amount is not known\nuntil after the process.\n\n- Before process: Call `GetRateLimits` with `Hits=0` to check the value of\n  `Remaining` counter.  If `Remaining` is zero, it's known\n  that the rate limit is depleted and the process can be aborted.\n- After process: Call `GetRateLimits` with a user specified `Hits` value.  If\n  the call returns over limit, the process cannot be aborted because it had\n  already completed.  Using `DRAIN_OVER_LIMIT` behavior, the `Remaining` count\n  will be drained to zero.\n\nOnce an over limit occurs in the \"After\" step, successive processes will detect\nthe over limit state in the \"Before\" step.\n\n## Gubernator as a library\nIf you are using golang, you can use Gubernator as a library. This is useful if\nyou wish to implement a rate limit service with your own company specific model\non top. We do this internally here at mailgun with a service we creatively\ncalled `ratelimits` which keeps track of the limits imposed on a per account\nbasis. In this way you can utilize the power and speed of Gubernator but still\nlayer business logic and integrate domain specific problems into your rate\nlimiting service.\n\nWhen you use the library, your service becomes a full member of the cluster\nparticipating in the same consistent hashing and caching as a stand alone\nGubernator server would. All you need to do is provide the GRPC server instance\nand tell Gubernator where the peers in your cluster are located. The\n`cmd/gubernator/main.go` is a great example of how to use Gubernator as a\nlibrary.\n\n### Optional Disk Persistence\nWhile the Gubernator server currently doesn't directly support disk\npersistence, the Gubernator library does provide interfaces through which\nlibrary users can implement persistence. The Gubernator library has two\ninterfaces available for disk persistence. Depending on the use case an\nimplementor can implement the [Loader](/store.go) interface and only support persistence\nof rate limits at startup and shutdown, or users can implement the [Store](/store.go)\ninterface and Gubernator will continuously call `OnChange()` and `Get()` to\nkeep the in memory cache and persistent store up to date with the latest rate\nlimit data. Both interfaces *can* be implemented simultaneously to ensure data\nis always saved to persistent storage.\n\nFor those who choose to implement the `Store` interface, it is not required to\nstore ALL the rate limits received via `OnChange()`. For instance; If you wish\nto support rate limit durations longer than a minute, day or month, calls to\n`OnChange()` can check the duration of a rate limit and decide to only persist\nthose rate limits that have durations over a self determined limit.\n\n### API\nAll methods are accessed via GRPC but are also exposed via HTTP using the\n[GRPC Gateway](https://github.com/grpc-ecosystem/grpc-gateway)\n\n#### Health Check\nHealth check returns `unhealthy` in the event a peer is reported by etcd or kubernetes\n as `up` but the server instance is unable to contact that peer via it's advertised address.\n\n###### GRPC\n```grpc\nrpc HealthCheck (HealthCheckReq) returns (HealthCheckResp)\n```\n\n###### HTTP\n```\nGET /v1/HealthCheck\n```\n\nExample response:\n\n```json\n{\n  \"status\": \"healthy\",\n  \"peer_count\": 3\n}\n```\n\n#### Get Rate Limit\nRate limits can be applied or retrieved using this interface. If the client\nmakes a request to the server with `hits: 0` then current state of the rate \nlimit is retrieved but not incremented.\n\n###### GRPC\n```grpc\nrpc GetRateLimits (GetRateLimitsReq) returns (GetRateLimitsResp)\n```\n\n###### HTTP\n```\nPOST /v1/GetRateLimits\n```\n\nExample Payload\n```json\n{\n  \"requests\": [\n    {\n      \"name\": \"requests_per_sec\",\n      \"uniqueKey\": \"account:12345\",\n      \"hits\": \"1\",\n      \"limit\": \"10\",\n      \"duration\": \"1000\"\n    }\n  ]\n}\n```\n\nExample response:\n\n```json\n{\n  \"responses\": [\n    {\n      \"status\": \"UNDER_LIMIT\",\n      \"limit\": \"10\",\n      \"remaining\": \"9\",\n      \"reset_time\": \"1690855128786\",\n      \"error\": \"\",\n      \"metadata\": {\n        \"owner\": \"gubernator:81\"\n      }\n    }\n  ]\n}\n```\n\n### Deployment\nNOTE: Gubernator uses `etcd`, Kubernetes or round-robin DNS to discover peers and\nestablish a cluster. If you don't have either, the docker-compose method is the\nsimplest way to try gubernator out.\n\n\n##### Docker with existing etcd cluster\n```bash\n$ docker run -p 8081:81 -p 9080:80 -e GUBER_ETCD_ENDPOINTS=etcd1:2379,etcd2:2379 \\\n   ghcr.io/mailgun/gubernator:latest\n\n# Hit the HTTP API at localhost:9080\n$ curl http://localhost:9080/v1/HealthCheck\n```\n\n##### Kubernetes\n```bash\n# Download the kubernetes deployment spec\n$ curl -O https://raw.githubusercontent.com/mailgun/gubernator/master/k8s-deployment.yaml\n\n# Edit the deployment file to change the environment config variables\n$ vi k8s-deployment.yaml\n\n# Create the deployment (includes headless service spec)\n$ kubectl create -f k8s-deployment.yaml\n```\n\n##### Round-robin DNS\nIf your DNS service supports auto-registration, for example AWS Route53 service discovery,\nyou can use same fully-qualified domain name to both let your business logic containers or\ninstances to find `gubernator` and for `gubernator` containers/instances to find each other.\n\n##### TLS\nGubernator supports TLS for both HTTP and GRPC connections. You can see an example with\nself signed certs by running `docker-compose-tls.yaml`\n```bash\n# Run docker compose\n$ docker-compose -f docker-compose-tls.yaml up -d\n\n# Hit the HTTP API at localhost:9080 (GRPC is at 9081)\n$ curl --cacert certs/ca.cert --cert certs/gubernator.pem --key certs/gubernator.key  https://localhost:9080/v1/HealthCheck\n```\n\n### Configuration\nGubernator is configured via environment variables with an optional `--config` flag\nwhich takes a file of key/values and places them into the local environment before startup.\n\nSee the `example.conf` for all available config options and their descriptions.\n\n### Architecture\nSee [architecture.md](docs/architecture.md) for a full description of the architecture and the inner \nworkings of gubernator.\n\n## Monitoring\nGubernator publishes Prometheus metrics for realtime monitoring.  See\n[prometheus.md](docs/prometheus.md) for details.\n\n## OpenTelemetry Tracing (OTEL)\nGubernator supports OpenTelemetry. See [tracing.md](docs/tracing.md) for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmailgun%2Fgubernator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmailgun%2Fgubernator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmailgun%2Fgubernator/lists"}