FA-73811 / Rate limiter algorithms / Open access
Combined per-second and per-minute fixed windows: minute window computed from milliseconds as seconds · case 01
The minute counter resets every 60 ms and the minute limit is never effective.
ROOT CAUSE
The minute index divides milliseconds by 60.
VERIFIED REPAIR
Use t div 60000.
Unsuccessful approach: Rounding moves minute boundaries to the half minute.
Case contract
Input {per_second, per_minute, requests [t_ms]} with nondecreasing t. Second windows are t div 1000 and minute windows t div 60000, each resetting its own counter only when its own window changes. The second limit is checked first ("deny-second"), then the minute limit ("deny-minute"); only admitted requests are counted, in both windows. Return [decisions, second count, minute count].
Why this case matters
Public APIs commonly publish several simultaneous limits; coupling their resets or charging denied requests to one of them breaks the published contract.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
ls, lm = x['per_second'], x['per_minute']
sw = mw = None
sc = mc = 0
out = []
for t in x['requests']:
s, m = t // 1000, t // 60
if s != sw:
sw, sc = s, 0
if m != mw:
mw, mc = m, 0
if sc >= ls:
out.append('deny-second')
elif mc >= lm:
out.append('deny-minute')
else:
sc += 1
mc += 1
out.append('allow')
return [out, sc, mc]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = [[['minute budget spans seconds',
{'per_minute': 5, 'per_second': 2, 'requests': [0, 1, 1000, 1001, 2000, 2001, 3001, 59999]},
[['allow', 'allow', 'allow', 'allow', 'allow', 'deny-minute', 'deny-minute', 'deny-minute'],
0,
5]],
['second limit inside minute',
{'per_minute': 10, 'per_second': 1, 'requests': [0, 10, 20, 1001, 1010]},
[['allow', 'deny-second', 'deny-second', 'allow', 'deny-second'], 1, 2]],
['minute rollover',
{'per_minute': 3, 'per_second': 3, 'requests': [59000, 59001, 59002, 59500, 60001, 60001]},
[['allow', 'allow', 'allow', 'deny-second', 'allow', 'allow'], 2, 2]],
['both exhausted reports second',
{'per_minute': 1, 'per_second': 1, 'requests': [0, 1, 1001, 1001]},
[['allow', 'deny-second', 'deny-minute', 'deny-minute'], 0, 1]],
['minute denials keep second budget',
{'per_minute': 2, 'per_second': 2, 'requests': [0, 1000, 2000, 2001, 2003, 60000, 60001, 60002]},
[['allow',
'allow',
'deny-minute',
'deny-minute',
'deny-minute',
'allow',
'allow',
'deny-second'],
2,
2]],
['random traffic',
{'per_minute': 6,
'per_second': 2,
'requests': [1062,
8418,
9273,
34515,
47777,
65888,
67598,
89351,
103730,
110167,
110678,
120637,
122553,
128547]},
[['allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'allow'],
1,
3]]],
[['minute budget spans seconds',
{'per_minute': 5, 'per_second': 2, 'requests': [0, 1, 1000, 1001, 2000, 2001, 3002, 59999]},
[['allow', 'allow', 'allow', 'allow', 'allow', 'deny-minute', 'deny-minute', 'deny-minute'],
0,
5]],
['second limit inside minute',
{'per_minute': 10, 'per_second': 1, 'requests': [0, 10, 20, 1002, 1010]},
[['allow', 'deny-second', 'deny-second', 'allow', 'deny-second'], 1, 2]],
['minute rollover',
{'per_minute': 3, 'per_second': 3, 'requests': [59000, 59001, 59002, 59500, 60002, 60001]},
[['allow', 'allow', 'allow', 'deny-second', 'allow', 'allow'], 2, 2]],
['both exhausted reports second',
{'per_minute': 1, 'per_second': 1, 'requests': [0, 1, 1002, 1001]},
[['allow', 'deny-second', 'deny-minute', 'deny-minute'], 0, 1]],
['minute denials keep second budget',
{'per_minute': 2, 'per_second': 2, 'requests': [0, 1000, 2000, 2001, 2004, 60000, 60001, 60002]},
[['allow',
'allow',
'deny-minute',
'deny-minute',
'deny-minute',
'allow',
'allow',
'deny-second'],
2,
2]],
['random traffic',
{'per_minute': 6,
'per_second': 2,
'requests': [4406,
11048,
15610,
37272,
37553,
45326,
49676,
64905,
66877,
80916,
104479,
105515,
113188,
115762]},
[['allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'deny-minute',
'allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'deny-minute'],
0,
6]]],
[['minute budget spans seconds',
{'per_minute': 5, 'per_second': 2, 'requests': [0, 1, 1000, 1001, 2000, 2001, 3003, 59999]},
[['allow', 'allow', 'allow', 'allow', 'allow', 'deny-minute', 'deny-minute', 'deny-minute'],
0,
5]],
['second limit inside minute',
{'per_minute': 10, 'per_second': 1, 'requests': [0, 10, 20, 1003, 1010]},
[['allow', 'deny-second', 'deny-second', 'allow', 'deny-second'], 1, 2]],
['minute rollover',
{'per_minute': 3, 'per_second': 3, 'requests': [59000, 59001, 59002, 59500, 60003, 60001]},
[['allow', 'allow', 'allow', 'deny-second', 'allow', 'allow'], 2, 2]],
['both exhausted reports second',
{'per_minute': 1, 'per_second': 1, 'requests': [0, 1, 1003, 1001]},
[['allow', 'deny-second', 'deny-minute', 'deny-minute'], 0, 1]],
['minute denials keep second budget',
{'per_minute': 2, 'per_second': 2, 'requests': [0, 1000, 2000, 2001, 2005, 60000, 60001, 60002]},
[['allow',
'allow',
'deny-minute',
'deny-minute',
'deny-minute',
'allow',
'allow',
'deny-second'],
2,
2]],
['random traffic',
{'per_minute': 6,
'per_second': 2,
'requests': [12496,
13001,
16785,
17435,
41410,
44765,
45872,
51289,
69195,
73570,
101381,
106192,
110461,
126847]},
[['allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'deny-minute',
'deny-minute',
'allow',
'allow',
'allow',
'allow',
'allow',
'allow'],
1,
1]]],
[['minute budget spans seconds',
{'per_minute': 5, 'per_second': 2, 'requests': [0, 1, 1000, 1001, 2000, 2001, 3004, 59999]},
[['allow', 'allow', 'allow', 'allow', 'allow', 'deny-minute', 'deny-minute', 'deny-minute'],
0,
5]],
['second limit inside minute',
{'per_minute': 10, 'per_second': 1, 'requests': [0, 10, 20, 1004, 1010]},
[['allow', 'deny-second', 'deny-second', 'allow', 'deny-second'], 1, 2]],
['minute rollover',
{'per_minute': 3, 'per_second': 3, 'requests': [59000, 59001, 59002, 59500, 60004, 60001]},
[['allow', 'allow', 'allow', 'deny-second', 'allow', 'allow'], 2, 2]],
['both exhausted reports second',
{'per_minute': 1, 'per_second': 1, 'requests': [0, 1, 1004, 1001]},
[['allow', 'deny-second', 'deny-minute', 'deny-minute'], 0, 1]],
['minute denials keep second budget',
{'per_minute': 2, 'per_second': 2, 'requests': [0, 1000, 2000, 2001, 2006, 60000, 60001, 60002]},
[['allow',
'allow',
'deny-minute',
'deny-minute',
'deny-minute',
'allow',
'allow',
'deny-second'],
2,
2]],
['random traffic',
{'per_minute': 6,
'per_second': 2,
'requests': [35872,
47171,
57795,
66404,
69658,
83185,
86836,
87855,
88307,
96707,
97802,
102613,
119928,
129412]},
[['allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'deny-minute',
'deny-minute',
'deny-minute',
'deny-minute',
'allow'],
1,
1]]],
[['minute budget spans seconds',
{'per_minute': 5, 'per_second': 2, 'requests': [0, 1, 1000, 1001, 2000, 2001, 3005, 59999]},
[['allow', 'allow', 'allow', 'allow', 'allow', 'deny-minute', 'deny-minute', 'deny-minute'],
0,
5]],
['second limit inside minute',
{'per_minute': 10, 'per_second': 1, 'requests': [0, 10, 20, 1005, 1010]},
[['allow', 'deny-second', 'deny-second', 'allow', 'deny-second'], 1, 2]],
['minute rollover',
{'per_minute': 3, 'per_second': 3, 'requests': [59000, 59001, 59002, 59500, 60005, 60001]},
[['allow', 'allow', 'allow', 'deny-second', 'allow', 'allow'], 2, 2]],
['both exhausted reports second',
{'per_minute': 1, 'per_second': 1, 'requests': [0, 1, 1005, 1001]},
[['allow', 'deny-second', 'deny-minute', 'deny-minute'], 0, 1]],
['minute denials keep second budget',
{'per_minute': 2, 'per_second': 2, 'requests': [0, 1000, 2000, 2001, 2007, 60000, 60001, 60002]},
[['allow',
'allow',
'deny-minute',
'deny-minute',
'deny-minute',
'allow',
'allow',
'deny-second'],
2,
2]],
['random traffic',
{'per_minute': 6,
'per_second': 2,
'requests': [2584,
8339,
26804,
29473,
39448,
51111,
57796,
65951,
70268,
73982,
76067,
80339,
81264,
102821]},
[['allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'deny-minute',
'allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'deny-minute'],
0,
6]]]]
for label, args, expected in cases[N - 1]:
check(label, solve(args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| minute budget spans seconds | [['allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow'], 1, 1] | [['allow', 'allow', 'allow', 'allow', 'allow', 'deny-minute', 'deny-minute', 'deny-minute'], 0, 5] | Failed |
| second limit inside minute | [['allow', 'deny-second', 'deny-second', 'allow', 'deny-second'], 1, 1] | [['allow', 'deny-second', 'deny-second', 'allow', 'deny-second'], 1, 2] | Failed |
| minute rollover | [['allow', 'allow', 'allow', 'deny-second', 'allow', 'allow'], 2, 2] | [['allow', 'allow', 'allow', 'deny-second', 'allow', 'allow'], 2, 2] | Passed |
| both exhausted reports second | [['allow', 'deny-second', 'allow', 'deny-second'], 1, 1] | [['allow', 'deny-second', 'deny-minute', 'deny-minute'], 0, 1] | Failed |
| minute denials keep second budget | [['allow', 'allow', 'allow', 'allow', 'deny-second', 'allow', 'allow', 'deny-second'], 2, 2] | [['allow', 'allow', 'deny-minute', 'deny-minute', 'deny-minute', 'allow', 'allow', 'deny-second'], 2, 2] | Failed |
| random traffic | [['allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow'], 1, 1] | [['allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow'], 1, 3] | Failed |
SHA-256 / 350ac19728f40aecccfa04eac5c99de6f346886988977ade1e65539ee53e08a1
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
ls, lm = x['per_second'], x['per_minute']
sw = mw = None
sc = mc = 0
out = []
for t in x['requests']:
s, m = t // 1000, round(t / 60000)
if s != sw:
sw, sc = s, 0
if m != mw:
mw, mc = m, 0
if sc >= ls:
out.append('deny-second')
elif mc >= lm:
out.append('deny-minute')
else:
sc += 1
mc += 1
out.append('allow')
return [out, sc, mc]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = [[['minute budget spans seconds',
{'per_minute': 5, 'per_second': 2, 'requests': [0, 1, 1000, 1001, 2000, 2001, 3001, 59999]},
[['allow', 'allow', 'allow', 'allow', 'allow', 'deny-minute', 'deny-minute', 'deny-minute'],
0,
5]],
['second limit inside minute',
{'per_minute': 10, 'per_second': 1, 'requests': [0, 10, 20, 1001, 1010]},
[['allow', 'deny-second', 'deny-second', 'allow', 'deny-second'], 1, 2]],
['minute rollover',
{'per_minute': 3, 'per_second': 3, 'requests': [59000, 59001, 59002, 59500, 60001, 60001]},
[['allow', 'allow', 'allow', 'deny-second', 'allow', 'allow'], 2, 2]],
['both exhausted reports second',
{'per_minute': 1, 'per_second': 1, 'requests': [0, 1, 1001, 1001]},
[['allow', 'deny-second', 'deny-minute', 'deny-minute'], 0, 1]],
['minute denials keep second budget',
{'per_minute': 2, 'per_second': 2, 'requests': [0, 1000, 2000, 2001, 2003, 60000, 60001, 60002]},
[['allow',
'allow',
'deny-minute',
'deny-minute',
'deny-minute',
'allow',
'allow',
'deny-second'],
2,
2]],
['random traffic',
{'per_minute': 6,
'per_second': 2,
'requests': [1062,
8418,
9273,
34515,
47777,
65888,
67598,
89351,
103730,
110167,
110678,
120637,
122553,
128547]},
[['allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'allow'],
1,
3]]],
[['minute budget spans seconds',
{'per_minute': 5, 'per_second': 2, 'requests': [0, 1, 1000, 1001, 2000, 2001, 3002, 59999]},
[['allow', 'allow', 'allow', 'allow', 'allow', 'deny-minute', 'deny-minute', 'deny-minute'],
0,
5]],
['second limit inside minute',
{'per_minute': 10, 'per_second': 1, 'requests': [0, 10, 20, 1002, 1010]},
[['allow', 'deny-second', 'deny-second', 'allow', 'deny-second'], 1, 2]],
['minute rollover',
{'per_minute': 3, 'per_second': 3, 'requests': [59000, 59001, 59002, 59500, 60002, 60001]},
[['allow', 'allow', 'allow', 'deny-second', 'allow', 'allow'], 2, 2]],
['both exhausted reports second',
{'per_minute': 1, 'per_second': 1, 'requests': [0, 1, 1002, 1001]},
[['allow', 'deny-second', 'deny-minute', 'deny-minute'], 0, 1]],
['minute denials keep second budget',
{'per_minute': 2, 'per_second': 2, 'requests': [0, 1000, 2000, 2001, 2004, 60000, 60001, 60002]},
[['allow',
'allow',
'deny-minute',
'deny-minute',
'deny-minute',
'allow',
'allow',
'deny-second'],
2,
2]],
['random traffic',
{'per_minute': 6,
'per_second': 2,
'requests': [4406,
11048,
15610,
37272,
37553,
45326,
49676,
64905,
66877,
80916,
104479,
105515,
113188,
115762]},
[['allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'deny-minute',
'allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'deny-minute'],
0,
6]]],
[['minute budget spans seconds',
{'per_minute': 5, 'per_second': 2, 'requests': [0, 1, 1000, 1001, 2000, 2001, 3003, 59999]},
[['allow', 'allow', 'allow', 'allow', 'allow', 'deny-minute', 'deny-minute', 'deny-minute'],
0,
5]],
['second limit inside minute',
{'per_minute': 10, 'per_second': 1, 'requests': [0, 10, 20, 1003, 1010]},
[['allow', 'deny-second', 'deny-second', 'allow', 'deny-second'], 1, 2]],
['minute rollover',
{'per_minute': 3, 'per_second': 3, 'requests': [59000, 59001, 59002, 59500, 60003, 60001]},
[['allow', 'allow', 'allow', 'deny-second', 'allow', 'allow'], 2, 2]],
['both exhausted reports second',
{'per_minute': 1, 'per_second': 1, 'requests': [0, 1, 1003, 1001]},
[['allow', 'deny-second', 'deny-minute', 'deny-minute'], 0, 1]],
['minute denials keep second budget',
{'per_minute': 2, 'per_second': 2, 'requests': [0, 1000, 2000, 2001, 2005, 60000, 60001, 60002]},
[['allow',
'allow',
'deny-minute',
'deny-minute',
'deny-minute',
'allow',
'allow',
'deny-second'],
2,
2]],
['random traffic',
{'per_minute': 6,
'per_second': 2,
'requests': [12496,
13001,
16785,
17435,
41410,
44765,
45872,
51289,
69195,
73570,
101381,
106192,
110461,
126847]},
[['allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'deny-minute',
'deny-minute',
'allow',
'allow',
'allow',
'allow',
'allow',
'allow'],
1,
1]]],
[['minute budget spans seconds',
{'per_minute': 5, 'per_second': 2, 'requests': [0, 1, 1000, 1001, 2000, 2001, 3004, 59999]},
[['allow', 'allow', 'allow', 'allow', 'allow', 'deny-minute', 'deny-minute', 'deny-minute'],
0,
5]],
['second limit inside minute',
{'per_minute': 10, 'per_second': 1, 'requests': [0, 10, 20, 1004, 1010]},
[['allow', 'deny-second', 'deny-second', 'allow', 'deny-second'], 1, 2]],
['minute rollover',
{'per_minute': 3, 'per_second': 3, 'requests': [59000, 59001, 59002, 59500, 60004, 60001]},
[['allow', 'allow', 'allow', 'deny-second', 'allow', 'allow'], 2, 2]],
['both exhausted reports second',
{'per_minute': 1, 'per_second': 1, 'requests': [0, 1, 1004, 1001]},
[['allow', 'deny-second', 'deny-minute', 'deny-minute'], 0, 1]],
['minute denials keep second budget',
{'per_minute': 2, 'per_second': 2, 'requests': [0, 1000, 2000, 2001, 2006, 60000, 60001, 60002]},
[['allow',
'allow',
'deny-minute',
'deny-minute',
'deny-minute',
'allow',
'allow',
'deny-second'],
2,
2]],
['random traffic',
{'per_minute': 6,
'per_second': 2,
'requests': [35872,
47171,
57795,
66404,
69658,
83185,
86836,
87855,
88307,
96707,
97802,
102613,
119928,
129412]},
[['allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'deny-minute',
'deny-minute',
'deny-minute',
'deny-minute',
'allow'],
1,
1]]],
[['minute budget spans seconds',
{'per_minute': 5, 'per_second': 2, 'requests': [0, 1, 1000, 1001, 2000, 2001, 3005, 59999]},
[['allow', 'allow', 'allow', 'allow', 'allow', 'deny-minute', 'deny-minute', 'deny-minute'],
0,
5]],
['second limit inside minute',
{'per_minute': 10, 'per_second': 1, 'requests': [0, 10, 20, 1005, 1010]},
[['allow', 'deny-second', 'deny-second', 'allow', 'deny-second'], 1, 2]],
['minute rollover',
{'per_minute': 3, 'per_second': 3, 'requests': [59000, 59001, 59002, 59500, 60005, 60001]},
[['allow', 'allow', 'allow', 'deny-second', 'allow', 'allow'], 2, 2]],
['both exhausted reports second',
{'per_minute': 1, 'per_second': 1, 'requests': [0, 1, 1005, 1001]},
[['allow', 'deny-second', 'deny-minute', 'deny-minute'], 0, 1]],
['minute denials keep second budget',
{'per_minute': 2, 'per_second': 2, 'requests': [0, 1000, 2000, 2001, 2007, 60000, 60001, 60002]},
[['allow',
'allow',
'deny-minute',
'deny-minute',
'deny-minute',
'allow',
'allow',
'deny-second'],
2,
2]],
['random traffic',
{'per_minute': 6,
'per_second': 2,
'requests': [2584,
8339,
26804,
29473,
39448,
51111,
57796,
65951,
70268,
73982,
76067,
80339,
81264,
102821]},
[['allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'deny-minute',
'allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'deny-minute'],
0,
6]]]]
for label, args, expected in cases[N - 1]:
check(label, solve(args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| minute budget spans seconds | [['allow', 'allow', 'allow', 'allow', 'allow', 'deny-minute', 'deny-minute', 'allow'], 1, 1] | [['allow', 'allow', 'allow', 'allow', 'allow', 'deny-minute', 'deny-minute', 'deny-minute'], 0, 5] | Failed |
| second limit inside minute | [['allow', 'deny-second', 'deny-second', 'allow', 'deny-second'], 1, 2] | [['allow', 'deny-second', 'deny-second', 'allow', 'deny-second'], 1, 2] | Passed |
| minute rollover | [['allow', 'allow', 'allow', 'deny-second', 'deny-minute', 'deny-minute'], 0, 3] | [['allow', 'allow', 'allow', 'deny-second', 'allow', 'allow'], 2, 2] | Failed |
| both exhausted reports second | [['allow', 'deny-second', 'deny-minute', 'deny-minute'], 0, 1] | [['allow', 'deny-second', 'deny-minute', 'deny-minute'], 0, 1] | Passed |
| minute denials keep second budget | [['allow', 'allow', 'deny-minute', 'deny-minute', 'deny-minute', 'allow', 'allow', 'deny-second'], 2, 2] | [['allow', 'allow', 'deny-minute', 'deny-minute', 'deny-minute', 'allow', 'allow', 'deny-second'], 2, 2] | Passed |
| random traffic | [['allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow'], 1, 6] | [['allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow'], 1, 3] | Failed |
SHA-256 / f07a38d1735eee978bd6baa8c5c72b408187d8f224a25bec1882ac6689da4d80
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(x):
ls, lm = x['per_second'], x['per_minute']
sw = mw = None
sc = mc = 0
out = []
for t in x['requests']:
s, m = t // 1000, t // 60000
if s != sw:
sw, sc = s, 0
if m != mw:
mw, mc = m, 0
if sc >= ls:
out.append('deny-second')
elif mc >= lm:
out.append('deny-minute')
else:
sc += 1
mc += 1
out.append('allow')
return [out, sc, mc]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = [[['minute budget spans seconds',
{'per_minute': 5, 'per_second': 2, 'requests': [0, 1, 1000, 1001, 2000, 2001, 3001, 59999]},
[['allow', 'allow', 'allow', 'allow', 'allow', 'deny-minute', 'deny-minute', 'deny-minute'],
0,
5]],
['second limit inside minute',
{'per_minute': 10, 'per_second': 1, 'requests': [0, 10, 20, 1001, 1010]},
[['allow', 'deny-second', 'deny-second', 'allow', 'deny-second'], 1, 2]],
['minute rollover',
{'per_minute': 3, 'per_second': 3, 'requests': [59000, 59001, 59002, 59500, 60001, 60001]},
[['allow', 'allow', 'allow', 'deny-second', 'allow', 'allow'], 2, 2]],
['both exhausted reports second',
{'per_minute': 1, 'per_second': 1, 'requests': [0, 1, 1001, 1001]},
[['allow', 'deny-second', 'deny-minute', 'deny-minute'], 0, 1]],
['minute denials keep second budget',
{'per_minute': 2, 'per_second': 2, 'requests': [0, 1000, 2000, 2001, 2003, 60000, 60001, 60002]},
[['allow',
'allow',
'deny-minute',
'deny-minute',
'deny-minute',
'allow',
'allow',
'deny-second'],
2,
2]],
['random traffic',
{'per_minute': 6,
'per_second': 2,
'requests': [1062,
8418,
9273,
34515,
47777,
65888,
67598,
89351,
103730,
110167,
110678,
120637,
122553,
128547]},
[['allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'allow'],
1,
3]]],
[['minute budget spans seconds',
{'per_minute': 5, 'per_second': 2, 'requests': [0, 1, 1000, 1001, 2000, 2001, 3002, 59999]},
[['allow', 'allow', 'allow', 'allow', 'allow', 'deny-minute', 'deny-minute', 'deny-minute'],
0,
5]],
['second limit inside minute',
{'per_minute': 10, 'per_second': 1, 'requests': [0, 10, 20, 1002, 1010]},
[['allow', 'deny-second', 'deny-second', 'allow', 'deny-second'], 1, 2]],
['minute rollover',
{'per_minute': 3, 'per_second': 3, 'requests': [59000, 59001, 59002, 59500, 60002, 60001]},
[['allow', 'allow', 'allow', 'deny-second', 'allow', 'allow'], 2, 2]],
['both exhausted reports second',
{'per_minute': 1, 'per_second': 1, 'requests': [0, 1, 1002, 1001]},
[['allow', 'deny-second', 'deny-minute', 'deny-minute'], 0, 1]],
['minute denials keep second budget',
{'per_minute': 2, 'per_second': 2, 'requests': [0, 1000, 2000, 2001, 2004, 60000, 60001, 60002]},
[['allow',
'allow',
'deny-minute',
'deny-minute',
'deny-minute',
'allow',
'allow',
'deny-second'],
2,
2]],
['random traffic',
{'per_minute': 6,
'per_second': 2,
'requests': [4406,
11048,
15610,
37272,
37553,
45326,
49676,
64905,
66877,
80916,
104479,
105515,
113188,
115762]},
[['allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'deny-minute',
'allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'deny-minute'],
0,
6]]],
[['minute budget spans seconds',
{'per_minute': 5, 'per_second': 2, 'requests': [0, 1, 1000, 1001, 2000, 2001, 3003, 59999]},
[['allow', 'allow', 'allow', 'allow', 'allow', 'deny-minute', 'deny-minute', 'deny-minute'],
0,
5]],
['second limit inside minute',
{'per_minute': 10, 'per_second': 1, 'requests': [0, 10, 20, 1003, 1010]},
[['allow', 'deny-second', 'deny-second', 'allow', 'deny-second'], 1, 2]],
['minute rollover',
{'per_minute': 3, 'per_second': 3, 'requests': [59000, 59001, 59002, 59500, 60003, 60001]},
[['allow', 'allow', 'allow', 'deny-second', 'allow', 'allow'], 2, 2]],
['both exhausted reports second',
{'per_minute': 1, 'per_second': 1, 'requests': [0, 1, 1003, 1001]},
[['allow', 'deny-second', 'deny-minute', 'deny-minute'], 0, 1]],
['minute denials keep second budget',
{'per_minute': 2, 'per_second': 2, 'requests': [0, 1000, 2000, 2001, 2005, 60000, 60001, 60002]},
[['allow',
'allow',
'deny-minute',
'deny-minute',
'deny-minute',
'allow',
'allow',
'deny-second'],
2,
2]],
['random traffic',
{'per_minute': 6,
'per_second': 2,
'requests': [12496,
13001,
16785,
17435,
41410,
44765,
45872,
51289,
69195,
73570,
101381,
106192,
110461,
126847]},
[['allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'deny-minute',
'deny-minute',
'allow',
'allow',
'allow',
'allow',
'allow',
'allow'],
1,
1]]],
[['minute budget spans seconds',
{'per_minute': 5, 'per_second': 2, 'requests': [0, 1, 1000, 1001, 2000, 2001, 3004, 59999]},
[['allow', 'allow', 'allow', 'allow', 'allow', 'deny-minute', 'deny-minute', 'deny-minute'],
0,
5]],
['second limit inside minute',
{'per_minute': 10, 'per_second': 1, 'requests': [0, 10, 20, 1004, 1010]},
[['allow', 'deny-second', 'deny-second', 'allow', 'deny-second'], 1, 2]],
['minute rollover',
{'per_minute': 3, 'per_second': 3, 'requests': [59000, 59001, 59002, 59500, 60004, 60001]},
[['allow', 'allow', 'allow', 'deny-second', 'allow', 'allow'], 2, 2]],
['both exhausted reports second',
{'per_minute': 1, 'per_second': 1, 'requests': [0, 1, 1004, 1001]},
[['allow', 'deny-second', 'deny-minute', 'deny-minute'], 0, 1]],
['minute denials keep second budget',
{'per_minute': 2, 'per_second': 2, 'requests': [0, 1000, 2000, 2001, 2006, 60000, 60001, 60002]},
[['allow',
'allow',
'deny-minute',
'deny-minute',
'deny-minute',
'allow',
'allow',
'deny-second'],
2,
2]],
['random traffic',
{'per_minute': 6,
'per_second': 2,
'requests': [35872,
47171,
57795,
66404,
69658,
83185,
86836,
87855,
88307,
96707,
97802,
102613,
119928,
129412]},
[['allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'deny-minute',
'deny-minute',
'deny-minute',
'deny-minute',
'allow'],
1,
1]]],
[['minute budget spans seconds',
{'per_minute': 5, 'per_second': 2, 'requests': [0, 1, 1000, 1001, 2000, 2001, 3005, 59999]},
[['allow', 'allow', 'allow', 'allow', 'allow', 'deny-minute', 'deny-minute', 'deny-minute'],
0,
5]],
['second limit inside minute',
{'per_minute': 10, 'per_second': 1, 'requests': [0, 10, 20, 1005, 1010]},
[['allow', 'deny-second', 'deny-second', 'allow', 'deny-second'], 1, 2]],
['minute rollover',
{'per_minute': 3, 'per_second': 3, 'requests': [59000, 59001, 59002, 59500, 60005, 60001]},
[['allow', 'allow', 'allow', 'deny-second', 'allow', 'allow'], 2, 2]],
['both exhausted reports second',
{'per_minute': 1, 'per_second': 1, 'requests': [0, 1, 1005, 1001]},
[['allow', 'deny-second', 'deny-minute', 'deny-minute'], 0, 1]],
['minute denials keep second budget',
{'per_minute': 2, 'per_second': 2, 'requests': [0, 1000, 2000, 2001, 2007, 60000, 60001, 60002]},
[['allow',
'allow',
'deny-minute',
'deny-minute',
'deny-minute',
'allow',
'allow',
'deny-second'],
2,
2]],
['random traffic',
{'per_minute': 6,
'per_second': 2,
'requests': [2584,
8339,
26804,
29473,
39448,
51111,
57796,
65951,
70268,
73982,
76067,
80339,
81264,
102821]},
[['allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'deny-minute',
'allow',
'allow',
'allow',
'allow',
'allow',
'allow',
'deny-minute'],
0,
6]]]]
for label, args, expected in cases[N - 1]:
check(label, solve(args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
| Boundary fixture | Actual | Expected | Outcome |
|---|---|---|---|
| minute budget spans seconds | [['allow', 'allow', 'allow', 'allow', 'allow', 'deny-minute', 'deny-minute', 'deny-minute'], 0, 5] | [['allow', 'allow', 'allow', 'allow', 'allow', 'deny-minute', 'deny-minute', 'deny-minute'], 0, 5] | Passed |
| second limit inside minute | [['allow', 'deny-second', 'deny-second', 'allow', 'deny-second'], 1, 2] | [['allow', 'deny-second', 'deny-second', 'allow', 'deny-second'], 1, 2] | Passed |
| minute rollover | [['allow', 'allow', 'allow', 'deny-second', 'allow', 'allow'], 2, 2] | [['allow', 'allow', 'allow', 'deny-second', 'allow', 'allow'], 2, 2] | Passed |
| both exhausted reports second | [['allow', 'deny-second', 'deny-minute', 'deny-minute'], 0, 1] | [['allow', 'deny-second', 'deny-minute', 'deny-minute'], 0, 1] | Passed |
| minute denials keep second budget | [['allow', 'allow', 'deny-minute', 'deny-minute', 'deny-minute', 'allow', 'allow', 'deny-second'], 2, 2] | [['allow', 'allow', 'deny-minute', 'deny-minute', 'deny-minute', 'allow', 'allow', 'deny-second'], 2, 2] | Passed |
| random traffic | [['allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow'], 1, 3] | [['allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow', 'allow'], 1, 3] | Passed |
SHA-256 / 249c4e2d1f111bba686a8a7437e3e994f711b586a147e9419ae887c2a1817a25
Verification & scope
A deterministic, bounded teaching model with stipulated constants and pre-hashed or explicitly hashed inputs; it is not a production implementation and makes no claim of conformance to any library or paper beyond the stated contract. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.
Observations recorded using Python 3.12.14 at 2026-09-29T14:48:51.005760+00:00.
Case digest / ecb32107c4a8a70101c3aa274b4fe31009fe035896f21716277a85a3c96f6f0e