limiters

repository·master·Indexed 20 days ago

https://github.com/mennanov/limiters

A Golang library of distributed rate limiting algorithms, including Token bucket, Leaky bucket, Fixed window counter, Sliding window counter, and Concurrent buffer. It supports multiple backends such as Redis, DynamoDB, Cosmos DB, Memcached, etcd, and in-memory storage, and provides distributed lock support via etcd, Consul, Zookeeper, Redis, Memcached, and PostgreSQL.

Tokens
11.1K
Snippets
43
Records
58
Agent score
70%

What's inside limiters

  1. Overview of rate limiting algorithms in limiters

    master

    The limiters library provides several distributed rate limiting algorithms for Golang, each with different characteristics and backend support:

    • Token bucket: Allows requests at a specific input rate with configurable bursts (via capacity). It is precise but requires a distributed lock. Supported backends: in-memory, redis, memcached, etcd, dynamodb, cosmos db.
    • Leaky bucket: Uses a FIFO queue to process requests at a constant rate. Input rate is only restricted by the queue capacity. Requires a lock. Supported backends: in-memory, redis, memcached, etcd, dynamodb, cosmos db.
    • Fixed window counter: A resource-efficient algorithm that does not require a lock. It may be lenient at window boundaries. Supported backends: in-memory, redis, memcached, dynamodb, cosmos db.
    • Sliding window counter: Smoothes out bursts at window boundaries by using two windows instead of one. It uses twice the memory of Fixed Window and may disallow all requests if a client is flooding the service. Supported backends: in-memory, redis, memcached, etcd, dynamodb, cosmos db.
    • Concurrent buffer: Allows concurrent requests up to a specified capacity. Requires a lock. Supported backends: in-memory, redis, memcached.
  2. Use distributed locks for consistency

    master

    Algorithms that require consistency during concurrent requests need a distributed lock. If your application runs as a single instance, you can use LockNoop as all algorithms are thread-safe.

    Supported distributed lock backends include:

    • etcd
    • Consul
    • Zookeeper
    • Redis
    • Memcached
    • PostgreSQL
  3. Use LeakyBucketStateBackend for state persistence

    master

    The LeakyBucketStateBackend interface allows you to choose where the bucket's state (the timestamp of the last request) is stored. This is essential for distributed rate limiting where multiple instances need to share the same bucket state.

    Available implementations:

    • LeakyBucketInMemory: For single-instance, non-persistent use.
    • LeakyBucketEtcd: Uses etcd for distributed state.
    • LeakyBucketRedis: Uses Redis for distributed state.
    • LeakyBucketMemcached: Uses Memcached for distributed state.
    • LeakyBucketDynamoDB: Uses AWS DynamoDB for distributed state.
    • LeakyBucketCosmosDB: Uses Azure Cosmos DB for distributed state.

    Most distributed backends support a raceCheck boolean. If set to true, the backend will return ErrRaceCondition if the state was modified by another process between the State() and SetState() calls, ensuring atomicity via versioning or CAS (Compare-And-Swap).

  4. Configure Azure Cosmos DB for NoSQL as a rate limiter backend

    master

    To use Azure Cosmos DB for NoSQL, you must create a database and a container beforehand.

    Requirements:

    • The container must have a default TTL set; otherwise, the TTL functionality will not work.
    • The partition key must be set to /partitionKey.
  5. Implement or use FixedWindowIncrementer backends

    master

    The FixedWindowIncrementer interface defines how the request counter is incremented for a specific time window. This allows the rate limiter to work in both single-node and distributed environments.

    Available implementations:

    • FixedWindowInMemory: For single-process, in-memory limiting.
    • FixedWindowRedis: Uses a Redis client (redis.UniversalClient) and a key prefix.
    • FixedWindowMemcached: Uses a Memcached client (*memcache.Client) and a key prefix.
    • FixedWindowDynamoDB: Uses an AWS DynamoDB client (*dynamodb.Client). Requires a table with a SortKey and TTL enabled.
    • FixedWindowCosmosDB: Uses an Azure Cosmos DB client (*azcosmos.ContainerClient).
  6. Configure DynamoDB as a rate limiter backend

    master

    Using DynamoDB requires a pre-existing table. Depending on the algorithm used, the table must have specific attributes:

    • Partition Key: String (Required for all backends)
    • Sort Key: String (Required for FixedWindow and SlidingWindow)
    • TTL: Number (Required for FixedWindow, SlidingWindow, LeakyBucket, and TokenBucket)

    You can provide a DynamoDBTableProperties struct manually or use LoadDynamoDBTableProperties(tableName) to fetch and verify the table configuration from AWS. Results from LoadDynamoDBTableProperties are cached.

  7. How the Sliding Window algorithm works

    master

    The SlidingWindow algorithm calculates a weighted total of requests based on the current and previous windows.

    1. It calls Increment on the backend to get the count for the prev and curr windows.
    2. It calculates the total using the formula: total = (prev * ttl / rate) + curr, where ttl is the time remaining in the current window.
    3. If total - capacity >= epsilon, the limit is exhausted.
    4. The Limit method returns the time.Duration the client should wait before retrying. If the request is allowed, it returns 0, nil.
  8. Use the DistLocker interface for distributed locking

    master

    The DistLocker interface provides a context-aware mechanism for distributed locking, similar to Go's sync.Locker. It is used to coordinate access to shared resources across multiple distributed instances. Implementations include support for Etcd, Consul, Zookeeper, Redis, Memcached, and PostgreSQL.

    To use a distributed lock, call Lock(ctx) to acquire the lock and Unlock(ctx) to release it. Always ensure Unlock is called (ideally via defer) to prevent deadlocks.

    // Example of using a DistLocker
    err := locker.Lock(ctx)
    if err != nil {
        return err
    }
    defer locker.Unlock(ctx)
    
    // Perform protected operation
  9. Implement or use a TokenBucketStateBackend

    master

    The TokenBucketStateBackend interface is used to persist the TokenBucketState (which contains Last timestamp and Available token count). This allows the rate limiter to work across multiple distributed instances.

    Available implementations include:

    • TokenBucketInMemory: Non-persistent, local to the instance.
    • TokenBucketEtcd: Uses etcd for storage.
    • TokenBucketRedis: Uses Redis for storage (supports a new JSON format and an old multi-key format).
    • TokenBucketMemcached: Uses Memcached.
    • TokenBucketDynamoDB: Uses AWS DynamoDB.
    • TokenBucketCosmosDB: Uses Azure Cosmos DB for NoSQL.
  10. Implement rate limiting with Azure Cosmos DB

    master
    The limiters package provides backend support for Azure Cosmos DB for NoSQL to implement distributed rate limiting. It utilizes a read-modify-write (RMW) pattern with optimistic concurrency control via ETags to ensure consistent count increments across distributed clients. When an update fails due to a precondition failure (ETag mismatch), the implementation automatically retries the operation.
  11. Set up testing infrastructure with Docker Compose

    master

    The project provides a docker-compose.yml file containing various backend services used for testing distributed rate limiters. You can use this file to spin up local instances of supported storage backends like Redis, Etcd, DynamoDB, and Cosmos DB emulator.

    docker-compose up