Rate limiting is one of the most critical components in backend systems. It protects APIs from abuse, ensures fair usage, and stabilizes systems under load.

In this series, we’ll go beyond theory and implement rate limiters in C#, while also understanding the real-world problems engineers face in production systems.

This is Part 1, where we’ll explore the Fixed Window Counter algorithm.

What is Rate Limiting?

Rate limiting controls how many requests a client can make in a given time window.

Example:

Without rate limiting:

Fixed Window Algorithm (Basic Idea)

The fixed window algorithm is the simplest rate limiter:

C# Implementation

Here’s the implementation you wrote:

Checkout github for code – https://github.com/sagarpanwarwork/RateLimitPlayground

class FixedWindowRateLimiter
{
    private readonly int _limit;
    private readonly TimeSpan _windowSize;

    private int _count;
    private DateTime _windowStart;

    public FixedWindowRateLimiter(int limit, TimeSpan windowSize)
    {
        _limit = limit;
        _windowSize = windowSize;
        _windowStart = DateTime.UtcNow;
        _count = 0;
    }

    public bool AllowRequest()
    {
        var now = DateTime.UtcNow;

        if (now - _windowStart >= _windowSize)
        {
            _windowStart = now;
            _count = 0;
        }

        if (_count < _limit)
        {
            _count++;
            return true;
        }

        return false;
    }
}

Testing the Rate Limiter

var limiter = new FixedWindowRateLimiter(3, TimeSpan.FromSeconds(10));

for (int i = 1; i <= 1000; i++)
{
Console.WriteLine($"Request {i}: {limiter.AllowRequest()}");
}

Real-World Problems (IMPORTANT)

This is where most tutorials stop. But real systems fail here.

Let’s go deeper.

Edge Case 1 — Boundary Burst Problem

This is the biggest flaw in fixed window rate limiting.

Scenario:

User sends:

Total = 6 requests in ~0.2 seconds

Why this happens:

Because the counter resets abruptly at window boundary.

Impact:

Edge Case 2 — Clock Precision

You’re using:

DateTime.UtcNow

Problem:

Real-world issue:

Better approach:

Use:

Edge Case 3 — Multi-threading (VERY IMPORTANT)

Your current code:

_count++;

Problem:

This is NOT thread-safe.

Scenario:

Multiple requests hit at same time:

👉 Race condition

Impact:

Fix (basic):

lock (this)
{
if (_count < _limit)
{
_count++;
return true;
}
}

Better approach:

Use:

Interlocked.Increment(ref _count);

Edge Case 4 — Per-User vs Global Limiter

Your limiter is:

var limiter = new FixedWindowRateLimiter(...)

This is global limiter

Problem:

One user can consume entire quota.

Real-world requirement:

You need per-user rate limiting

Solution:

Use dictionary:

Dictionary<string, FixedWindowRateLimiter>

Key = userId / IP

Example:

Edge Case 5 — Window Alignment Problem

Current logic:

_windowStart = now;

Problem:

Each user has a different window start.

Result:

Better approach:

Align windows:

var alignedWindow = now.Ticks / _windowSize.Ticks;

👉 This ensures:

Summary of Fixed Window Problems

IssueImpact
Boundary burstTraffic spikes
Clock precisionInconsistent behavior
Multi-threadingRace conditions
Global limiterUnfair usage
Window alignmentUnpredictable limits

When Should You Use Fixed Window?

Use it when:

✔ Simplicity is priority
✔ Low traffic system
✔ Not highly critical

Avoid when:

❌ High concurrency
❌ Financial systems
❌ Public APIs

Final Thought

Most developers implement rate limiting like this and stop.

Real engineers ask:

“What breaks in production?”

If you understand these edge cases, you’re not just coding—you’re designing systems.

Leave a Reply

Your email address will not be published. Required fields are marked *