fix(ops_run): unique triggering_event_id per cron fire
Cron schedules always passed trigger_key="scheduled", so ops_run
idempotency collapsed every weekday into one key. After the first fire,
create_ops_run was a silent no-op, the claim loop starved, and dual-clock
host timers produced empty FI briefs.
Map scheduled fires to run_id (or scheduled:{iso}) via
emit_triggering_event_id; log duplicate skips; document the contract.
This commit is contained in:
parent
00da21e39e
commit
21dc228cb1
6 changed files with 117 additions and 7 deletions
|
|
@ -11,6 +11,7 @@ import pytest
|
|||
|
||||
from activity_core.ops_run_queue import (
|
||||
build_idempotency_key,
|
||||
emit_triggering_event_id,
|
||||
max_attempts,
|
||||
ops_run_queue_enabled,
|
||||
ops_run_to_dict,
|
||||
|
|
@ -39,6 +40,58 @@ def test_build_idempotency_key() -> None:
|
|||
)
|
||||
|
||||
|
||||
def test_emit_triggering_event_id_scheduled_uses_run_id() -> None:
|
||||
"""Bare 'scheduled' must not be the ops_run key for every weekday fire."""
|
||||
day1 = emit_triggering_event_id("scheduled", "run-day-1")
|
||||
day2 = emit_triggering_event_id("scheduled", "run-day-2")
|
||||
assert day1 == "run-day-1"
|
||||
assert day2 == "run-day-2"
|
||||
assert day1 != day2
|
||||
|
||||
|
||||
def test_emit_triggering_event_id_prefers_scheduled_for() -> None:
|
||||
got = emit_triggering_event_id(
|
||||
"scheduled",
|
||||
"run-fallback",
|
||||
scheduled_for="2026-08-05T05:30:00+00:00",
|
||||
)
|
||||
assert got == "scheduled:2026-08-05T05:30:00+00:00"
|
||||
|
||||
|
||||
def test_emit_triggering_event_id_passes_through_events_and_manual() -> None:
|
||||
assert emit_triggering_event_id("evt-abc", "run-x") == "evt-abc"
|
||||
assert (
|
||||
emit_triggering_event_id("manual-3da4cf06", "run-y") == "manual-3da4cf06"
|
||||
)
|
||||
|
||||
|
||||
def test_scheduled_fires_produce_distinct_ops_keys() -> None:
|
||||
"""Regression: two cron days must not share one ops_run idempotency key."""
|
||||
def_id = "3169ab1f-882b-59c7-9763-d014dc96f4fc"
|
||||
source = "emit-fi-daily-brief-task"
|
||||
key_aug4 = build_idempotency_key(
|
||||
TaskSpec(
|
||||
title="FI",
|
||||
activity_definition_id=def_id,
|
||||
source_id=source,
|
||||
triggering_event_id=emit_triggering_event_id("scheduled", "run-aug4"),
|
||||
)
|
||||
)
|
||||
key_aug5 = build_idempotency_key(
|
||||
TaskSpec(
|
||||
title="FI",
|
||||
activity_definition_id=def_id,
|
||||
source_id=source,
|
||||
triggering_event_id=emit_triggering_event_id("scheduled", "run-aug5"),
|
||||
)
|
||||
)
|
||||
# Old bug: both would be "...:scheduled"
|
||||
legacy = f"{def_id}:{source}:scheduled"
|
||||
assert key_aug4 != key_aug5
|
||||
assert key_aug4 != legacy
|
||||
assert key_aug5 != legacy
|
||||
|
||||
|
||||
def test_max_attempts(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.delenv("OPS_RUN_MAX_ATTEMPTS", raising=False)
|
||||
assert max_attempts() == 3
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue