Logo
ProgramsThoughtsTechMock InterviewsAboutGet started

Connect with me

May 5, 2026

Rate Limiting in C# (Part 1): Fixed Window Algorithm Explained with Real-World Edge Cases

Sagar PanwarSagar Panwar
Rate Limiting in C# (Part 1): Fixed Window Algorithm Explained with Real-World Edge Cases

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:

  • 100 requests per minute
  • 10 requests per second

Without rate limiting:

  • APIs can be abused
  • Systems can crash
  • Resources can be exhausted

Fixed Window Algorithm (Basic Idea)

The fixed window algorithm is the simplest rate limiter:

  • Count requests within a fixed time window
  • Reset the counter after the window expires

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:

  • Limit: 3 requests / 10 seconds

User sends:

  • 3 requests at 9.9 sec
  • 3 requests at 10.1 sec

Total = 6 requests in ~0.2 seconds

Why this happens:

Because the counter resets abruptly at window boundary.

Impact:

  • Traffic spikes
  • API overload
  • Unfair usage

Edge Case 2 — Clock Precision

You’re using:

DateTime.UtcNow

Problem:

  • Not monotonic
  • Can change due to system clock sync
  • Low precision in high-frequency systems

Real-world issue:

  • Requests may fall into wrong window
  • Inconsistent behavior

Better approach:

Use:

  • Stopwatch (monotonic time)
  • Or high-resolution timers

Edge Case 3 — Multi-threading (VERY IMPORTANT)

Your current code:

_count++;

Problem:

This is NOT thread-safe.

Scenario:

Multiple requests hit at same time:

  • Thread A reads _count = 2
  • Thread B reads _count = 2
  • Both increment → _count = 3 (expected 4)

👉 Race condition

Impact:

  • More requests allowed than limit
  • Security issue

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:

  • User A → 3 requests allowed
  • User B → 3 requests allowed

Edge Case 5 — Window Alignment Problem

Current logic:

_windowStart = now;

Problem:

Each user has a different window start.

Result:

  • No consistent boundaries
  • Hard to debug
  • Inconsistent throttling

Better approach:

Align windows:

var alignedWindow = now.Ticks / _windowSize.Ticks;

👉 This ensures:

  • Fixed global windows
  • Predictable behavior

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.