FAILURE MAP
← Case archive

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.

Verified by executionVariant 1 · 6 checks per implementationDownload source bundle ↓JSON ↗

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 fixtureActualExpectedOutcome
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 fixtureActualExpectedOutcome
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 fixtureActualExpectedOutcome
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