bohdan_v16 May 2026 03:46

Fixed window rate limiters have a well-known burst problem: a user can make double the allowed requests by sending them right at the boundary between two windows. Sliding window fixes this and is still straightforward to implement without Redis.

PHP
<?php
class SlidingWindowRateLimiter
{
private array $log = []; // [key => [timestamps]]
public function __construct(
private readonly int $limit,
private readonly int $windowSeconds,
) {}
public function isAllowed(string $key): bool
{
$now = microtime(true);
$cutoff = $now - $this->windowSeconds;
if (!isset($this->log[$key])) {
$this->log[$key] = [];
}
// Remove timestamps outside the window
$this->log[$key] = array_filter(
$this->log[$key],
fn($t) => $t > $cutoff
);
if (count($this->log[$key]) >= $this->limit) {
return false;
}
$this->log[$key][] = $now;
return true;
}
public function remaining(string $key): int
{
$now = microtime(true);
$cutoff = $now - $this->windowSeconds;
$active = array_filter($this->log[$key] ?? [], fn($t) => $t > $cutoff);
return max(0, $this->limit - count($active));
}
}
// 5 requests per 10 seconds
 
הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

The key difference from token bucket: sliding window counts exact request timestamps, not tokens. It is more precise for “N requests per period” semantics but uses more memory per key since you store all timestamps in the window.

Replies (1)
petr_sys16 May 2026 04:19

Nice writeup. The memory issue you mentioned is real at scale. For 10 req/min limit with 1 million active users you would store up to 10 million timestamps in memory. In practice for distributed systems this goes to Redis with a sorted set, but the in-memory version is perfect for CLI tools, test suites, or single-process applications.

One micro-optimization: instead of array_filter + count, you can use array_splice to remove old entries in-place which avoids creating a new array:

PHP
<?php
// In-place removal from the front of the log
function pruneLog(array &$log, float $cutoff): void
{
while (!empty($log) && $log[0] <= $cutoff) {
array_shift($log);
}
}
// Works correctly when timestamps are added in chronological order
// (which they always are in practice)
$log = [1.0, 2.0, 3.0, 7.0, 8.0];
$now = 10.0;
$window = 5.0;
pruneLog($log, $now - $window);
print_r($log); // [7.0, 8.0]
הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
0
Write a reply
Markdown. ```php blocks are runnable.