Skip to content

Commit e9a9566

Browse files
authored
Check the activate_in/activate_at gates on the database clock instead of sleeping (#264)
* Check the activate_in gate on the database clock instead of sleeping The test asserted nothing arrived 200ms after publishing a row due in 500ms. The gate is server-side (now() + 500ms at insert), so a slow commit or an oversleeping event loop under load could push the check past it and fail with the library behaving correctly. Assert the claimed row's own timestamps instead: next_attempt_at is exactly 500ms after created_at, and acquired_at is not before next_attempt_at. * Check the activate_at gate on the database clock instead of sleeping
1 parent 680a81a commit e9a9566

1 file changed

Lines changed: 23 additions & 17 deletions

File tree

‎tests/test_integration.py‎

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
make_outbox_table,
2828
outbox_autovacuum_ddl,
2929
)
30+
from faststream_outbox.annotations import OutboxMessage as AnnotatedOutboxMessage
3031
from faststream_outbox.client import OutboxClient
3132
from faststream_outbox.envelope import _encode_payload as encode_payload
3233
from faststream_outbox.message import OutboxInnerMessage
@@ -997,13 +998,14 @@ async def handle(body: dict) -> None:
997998

998999

9991000
async def test_publish_with_activate_in_delays_delivery(pg_engine, outbox_table) -> None:
1000-
"""Handler must not see the message until activate_in elapses."""
1001-
received: list[dict] = []
1001+
"""The row is claimed no earlier than activate_in after its insert, by the database clock."""
1002+
claimed: list[OutboxInnerMessage] = []
10021003
broker = OutboxBroker(pg_engine, outbox_table=outbox_table)
10031004

10041005
@broker.subscriber("orders", min_fetch_interval=0.05, max_fetch_interval=0.2)
1005-
async def handle(body: dict) -> None:
1006-
received.append(body)
1006+
async def handle(body: dict, msg: AnnotatedOutboxMessage) -> None:
1007+
assert body == {"order_id": 1}
1008+
claimed.append(msg.raw_message)
10071009

10081010
session_factory = async_sessionmaker(pg_engine, expire_on_commit=False)
10091011
async with broker:
@@ -1014,21 +1016,23 @@ async def handle(body: dict) -> None:
10141016
session=session,
10151017
activate_in=dt.timedelta(milliseconds=500),
10161018
)
1017-
# Before the gate opens: nothing delivered.
1018-
await asyncio.sleep(0.2)
1019-
assert received == []
1020-
# After the gate opens: delivered.
1021-
await _wait_until(lambda: received, timeout=3.0)
1022-
assert received == [{"order_id": 1}]
1019+
await _wait_until(lambda: claimed, timeout=3.0)
1020+
1021+
[row] = claimed
1022+
assert row.next_attempt_at - row.created_at == dt.timedelta(milliseconds=500)
1023+
assert row.acquired_at is not None
1024+
assert row.acquired_at >= row.next_attempt_at
10231025

10241026

10251027
async def test_publish_with_activate_at_delays_delivery(pg_engine, outbox_table) -> None:
1026-
received: list[dict] = []
1028+
"""The row is gated at activate_at and claimed no earlier, by the database clock."""
1029+
claimed: list[OutboxInnerMessage] = []
10271030
broker = OutboxBroker(pg_engine, outbox_table=outbox_table)
10281031

10291032
@broker.subscriber("orders", min_fetch_interval=0.05, max_fetch_interval=0.2)
1030-
async def handle(body: dict) -> None:
1031-
received.append(body)
1033+
async def handle(body: dict, msg: AnnotatedOutboxMessage) -> None:
1034+
assert body == {"order_id": 2}
1035+
claimed.append(msg.raw_message)
10321036

10331037
fire_at = dt.datetime.now(tz=dt.UTC) + dt.timedelta(milliseconds=500)
10341038
session_factory = async_sessionmaker(pg_engine, expire_on_commit=False)
@@ -1040,10 +1044,12 @@ async def handle(body: dict) -> None:
10401044
session=session,
10411045
activate_at=fire_at,
10421046
)
1043-
await asyncio.sleep(0.2)
1044-
assert received == []
1045-
await _wait_until(lambda: received, timeout=3.0)
1046-
assert received == [{"order_id": 2}]
1047+
await _wait_until(lambda: claimed, timeout=3.0)
1048+
1049+
[row] = claimed
1050+
assert row.next_attempt_at == fire_at
1051+
assert row.acquired_at is not None
1052+
assert row.acquired_at >= row.next_attempt_at
10471053

10481054

10491055
async def test_publish_rejects_activate_in_and_at_together(pg_engine, outbox_table) -> None:

0 commit comments

Comments
 (0)