@@ -2005,27 +2005,36 @@ async def test_a_worker_with_one_slot_still_visits_every_table(session_factory):
20052005 await engine .dispose ()
20062006
20072007
2008- async def test_a_rate_limit_lets_through_a_burst_then_refills (session_factory ):
2008+ @pytest .mark .parametrize ("enqueue_delay" , [0 , 1.1 ])
2009+ async def test_a_rate_limit_lets_through_a_burst_then_refills (
2010+ session_factory , monkeypatch , enqueue_delay
2011+ ):
2012+ """A full bucket allows a burst and a partial refill allows two more runs."""
20092013 STARTS .clear ()
20102014 provider = f"provider-{ uuid .uuid4 ().hex } "
2011- started = time .monotonic ()
2015+ # Four tokens per hour prevent setup or scheduling delays from refilling
2016+ # the bucket; backdating it by half an hour releases exactly two tokens.
2017+ period = datetime .timedelta (hours = 1 )
2018+ monkeypatch .setattr (
2019+ Metered , "__workflow_limit__" , Limit (by = "provider" , rate = 4 , per = period )
2020+ )
2021+ await asyncio .sleep (enqueue_delay )
20122022 await start_many (Metered , provider , 8 , provider = provider )
20132023
2014- async def all_done () -> bool :
2015- """Tell whether every run has finished.
2016-
2017- Returns:
2018- Whether it has.
2019- """
2024+ for expected in (4 , 6 , 8 ):
2025+ if expected > 4 :
2026+ async with session_factory () as session , session .begin ():
2027+ await session .execute (
2028+ update (WorkflowRate )
2029+ .where (WorkflowRate .key == claim .bucket_key (Metered , provider ))
2030+ .values (tokens = 0 , updated_at = func .now () - period / 2 )
2031+ )
2032+ # A settled worker has finished its steps and found no more to claim.
2033+ assert await runner .wake (datetime .timedelta (seconds = 30 ))
2034+ assert len (STARTS [provider ]) == expected
20202035 rows = await Metered .by (Metered .provider == provider ).all ()
2021- return len (rows ) == 8 and all (row .status == "done" for row in rows )
2022-
2023- await wait_until (all_done , timeout = 60 )
2024- times = sorted (t - started for t in STARTS [provider ])
2025- # Four at once from a full bucket, then the rest as it refills: two a second.
2026- assert len (times ) == 8
2027- assert times [3 ] < 1
2028- assert times [7 ] >= 1.5
2036+ assert len (rows ) == 8
2037+ assert sum (row .status == "done" for row in rows ) == expected
20292038
20302039
20312040async def test_a_rate_limit_holds_across_workers (session_factory ):
0 commit comments