Home

Creating a Simple Rate Limiter using Token Bucket Algorithm in Zig

48 views

A rate limiter in Zig, like in other programming languages, is used to control the number of requests a user can make to a resource within a specific time frame. Zig is a general-purpose programming language designed for robustness, optimality, and maintainability. Let's implement a simple rate limiter using the token bucket algorithm in Zig.

Step-by-Step Implementation

  1. Define the Rate Limiter Struct

    • Create a structure to hold information about the rate limiter, such as the tokens and the interval for token replenishment.
  2. Initialize the Rate Limiter

    • Set up initial tokens, rate, and interval for token replenishment.
  3. Create Methods for the Rate Limiter

    • Implement methods to check if a request can proceed (allow) and to manage the replenishment of tokens.

Here is an example of a simple token bucket rate limiter in Zig:

const std = @import("std");

const RateLimiter = struct {
    tokens: usize,
    max_tokens: usize,
    interval: std.time.Duration,
    last_check: std.time.Timestamp,

    // Initializes a new Rate Limiter
    pub fn init(self: *RateLimiter, max_tokens: usize, interval: std.time.Duration) void {
        self.tokens = max_tokens;
        self.max_tokens = max_tokens;
        self.interval = interval;
        self.last_check = std.time.timestamp();
    }

    // Method to check if a request can proceed
    pub fn allow(self: *RateLimiter) !bool {
        const now = std.time.timestamp();
        const time_passed = now - self.last_check;

        if (time_passed >= self.interval) {
            const new_tokens = time_passed / self.interval;
            self.tokens = std.math.min(self.tokens + new_tokens, self.max_tokens);
            self.last_check = now;
        }

        if (self.tokens > 0) {
            self.tokens -= 1;
            return true;
        } else {
            return false;
        }
    }
};

pub fn main() !void {
    const allocator = std.heap.page_allocator;
    const rateLimiter = try allocator.create(RateLimiter);
    defer allocator.destroy(rateLimiter);

    rateLimiter.init(5, std.time.ns_per_s);

    for (i in 0..10) {
        if (try rateLimiter.allow()) {
            std.debug.print("Request {} allowed\n", .{i});
        } else {
            std.debug.print("Request {} denied\n", .{i});
        }
        std.time.sleep(std.time.ns_per_s / 5); // Simulate some processing
    }
}

Explanation

  1. RateLimiter struct: This holds the context for our rate limiter, including the number of tokens available, the maximum number of tokens, the interval duration for token replenishment, and the timestamp of the last check.

  2. init function: Sets up the rate limiter with a specific number of maximum tokens and a replenishment interval.

  3. allow method: Checks if a request can proceed by:

    • Calculating the time passed since the last check and replenishing tokens proportionally up to the maximum limit.
    • Decrementing a token if available and returning true (allowed) or false (denied).
  4. main function: Demonstrates the rate limiter by processing 10 requests in a loop, with a delay between each to simulate real-world usage.

This example provides a basic implementation of a rate limiter in Zig. The algorithm can be extended to support more sophisticated rate limiting techniques, such as leaky bucket or fixed window algorithms, depending on the requirements.