Repository navigation
fix(pipeline): age-gate ancient provider and fresh-enrichment sweep wakes #24714
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 3 commits
8ae0cc9
880b8c2
ece6d2c
4235431
eee5427
3ae885e
30ff8de
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| """Live sweep admission limits; never alter durable obligations or initial enqueue.""" | ||
|
|
||
| import math | ||
| import os | ||
|
|
||
| DEFAULT_MAX_AGE_DAYS = 14.0 | ||
|
|
||
|
|
||
| def enabled() -> bool: | ||
| """Default on; only explicit off restores the previous sweep behavior.""" | ||
| return os.getenv('BATCH_PIPELINE_SWEEP_AGE_GATE', 'on').strip().lower() != 'off' | ||
|
|
||
|
|
||
| def _days(raw: str) -> float: | ||
| try: | ||
| days = float(raw) | ||
| except ValueError: | ||
| return DEFAULT_MAX_AGE_DAYS | ||
| return days if math.isfinite(days) and 0 < days <= 3650 else DEFAULT_MAX_AGE_DAYS | ||
|
|
||
|
|
||
| def max_age_seconds(queue: str) -> float: | ||
| if queue == 'provider-stt': | ||
| days = _days(os.getenv('BATCH_PIPELINE_PROVIDER_STT_SWEEP_MAX_AGE_DAYS', '14')) | ||
| elif queue == 'enrichment-fresh': | ||
| days = _days(os.getenv('BATCH_PIPELINE_ENRICHMENT_FRESH_SWEEP_MAX_AGE_DAYS', '14')) | ||
| else: | ||
| raise ValueError('unknown sweep age queue') | ||
| return days * 86400 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,6 +19,7 @@ | |
| from typing import Any, Callable, Literal, Mapping | ||
|
|
||
| from database.outbox import Claim, EnqueueResult, OutboxKind, Outcome, SweepCounts | ||
| from utils.observability.sweep_age import skip_ancient | ||
|
|
||
| Row = dict[str, Any] | ||
| _ROW_ID = re.compile('[0-9a-f]{64}') | ||
|
|
@@ -320,6 +321,8 @@ def sweep(self, *, now: float, limit: int = 100) -> SweepCounts: | |
| or now - float(row.get('woken_at') or 0) < REWAKE_AFTER_SECONDS | ||
| ): | ||
| continue | ||
| if skip_ancient(row, 'provider-stt', now=now): | ||
| continue | ||
|
Comment on lines
+336
to
+337
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When the first 100 open provider rows are older than the configured age limit, this new skip leaves them permanently occupying the entire sweep page. The query above filters only on Useful? React with 👍 / 👎.
Comment on lines
+336
to
+337
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This changes provider-STT recovery and enrichment lifecycle behavior by enabling an age gate by default, but the commit adds no pipeline change note. AGENTS.md reference: backend/AGENTS.md:L164-L164 Useful? React with 👍 / 👎. |
||
| if row.get('lease_token'): | ||
| expired += 1 | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -220,3 +220,43 @@ def test_wake_task_shape(monkeypatch): | |||||||
| monkeypatch.delenv('PROVIDER_STT_TASKS_QUEUE') | ||||||||
| with pytest.raises(RuntimeError): | ||||||||
| provider_stt_tasks.create_wake(ROW, 'a' * 32, due_at=0) | ||||||||
|
|
||||||||
|
|
||||||||
| @pytest.mark.parametrize( | ||||||||
| 'age,setting,skipped', [(15, 'on', True), (1, 'on', False), (14, 'on', False), (15, 'off', False)] | ||||||||
| ) | ||||||||
| def test_sweep_age_gate_preserves_old_rows_and_counts(box, monkeypatch, age, setting, skipped): | ||||||||
| from copy import deepcopy | ||||||||
| from utils.observability.sweep_age import SKIPPED | ||||||||
|
|
||||||||
| monkeypatch.setenv('BATCH_PIPELINE_SWEEP_AGE_GATE', setting) | ||||||||
| monkeypatch.delenv('BATCH_PIPELINE_PROVIDER_STT_SWEEP_MAX_AGE_DAYS', raising=False) | ||||||||
| enqueue(box, now=100) | ||||||||
| box.woken.clear() | ||||||||
| before = deepcopy(row(box)) | ||||||||
| versions = dict(box.client.versions) | ||||||||
| counter = SKIPPED.labels(queue='provider-stt', reason='max_age') | ||||||||
| count = counter._value.get() | ||||||||
| result = box.outbox.sweep(now=100 + age * 86400) | ||||||||
| assert result.rewoken == (0 if skipped else 1) | ||||||||
| assert result.errors == 0 | ||||||||
| assert len(box.woken) == (0 if skipped else 1) | ||||||||
| assert counter._value.get() - count == int(skipped) | ||||||||
| if skipped: | ||||||||
| assert row(box) == before | ||||||||
| assert box.client.versions == versions | ||||||||
|
|
||||||||
|
|
||||||||
| def test_ancient_sweep_keeps_existing_privacy_discard(box, monkeypatch): | ||||||||
| from utils.observability.sweep_age import SKIPPED | ||||||||
|
|
||||||||
| monkeypatch.setenv('BATCH_PIPELINE_SWEEP_AGE_GATE', 'on') | ||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P3: Clear the provider-specific age override in this test so its 15-day fixture consistently uses the 14-day default and remains hermetic. Prompt for AI agents
Suggested change
|
||||||||
| enqueue(box, now=100) | ||||||||
| box.blocked.add(ROW) | ||||||||
| box.woken.clear() | ||||||||
| counter = SKIPPED.labels(queue='provider-stt', reason='max_age') | ||||||||
| before = counter._value.get() | ||||||||
| counts = box.outbox.sweep(now=100 + 15 * 86400) | ||||||||
| assert counts.discarded == 1 and counts.rewoken == 0 | ||||||||
| assert row(box)['closed_reason'] == 'privacy_blocked' | ||||||||
| assert not box.woken and counter._value.get() == before | ||||||||
Uh oh!
There was an error while loading. Please reload this page.