Rate Limiting in C# (Part 1): Fixed Window Algorithm Explained with Real-World Edge Cases
Sagar PanwarRate 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
| Issue | Impact |
|---|---|
| Boundary burst | Traffic spikes |
| Clock precision | Inconsistent behavior |
| Multi-threading | Race conditions |
| Global limiter | Unfair usage |
| Window alignment | Unpredictable 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.

