Skip to content

Commit 72bc744

Browse files
committed
fix(pipeline): recheck bucket quorum after promotion
Hold a community-only row again when bucketed reports appear below the three-bucket floor. Cover separate drains and a mixed same-drain queue.
1 parent 19bde79 commit 72bc744

3 files changed

Lines changed: 26 additions & 3 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ All notable changes to CallShield will be documented in this file.
66

77
- New community-only numbers now wait for reports on two UTC days before they
88
enter the shipped database. Reports with reporter buckets also need three
9-
distinct buckets. Pending reports expire after 30 days. The first pass held
9+
distinct buckets, even when bucketed reports arrive after an earlier
10+
bucketless promotion. Pending reports expire after 30 days. The first pass held
1011
449 older, uncorroborated rows, leaving 51,357 numbers in feed v47.
1112
- Setup now finishes once the required phone, message and call screening access
1213
is ready. You can skip notifications, caller ID overlay and notification

‎scripts/merge_community_reports.py‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -402,11 +402,12 @@ def main(argv: list[str] | None = None):
402402
pending.pop(number, None)
403403
continue
404404
state = pending.setdefault(number, legacy_community_state(entry, today))
405-
if state["published"]:
405+
if state["published"] and not any(event["bucket"] for event in state["events"]):
406406
continue
407407
if community_has_quorum(state):
408408
state["published"] = True
409409
else:
410+
state["published"] = False
410411
del existing[number]
411412
demoted += 1
412413

@@ -507,6 +508,10 @@ def main(argv: list[str] | None = None):
507508
if state:
508509
state["events"].append({"key": key, "day": reported_at, "bucket": bucket, "count": 1})
509510
state["entry"] = entry
511+
if bucket and state["published"] and not community_has_quorum(state):
512+
state["published"] = False
513+
del existing[number]
514+
demoted += 1
510515
updated += 1
511516
else:
512517
if state is None:

‎scripts/test_report_pipeline.py‎

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ def assert_community_promotion(data_dir: Path) -> None:
228228
legacy_number = "+12122340671"
229229
bucket_number = "+12122340672"
230230
old_number = "+12122340673"
231+
mixed_batch_number = "+12122340674"
231232

232233
write_report(data_dir, "first.json", legacy_number, None, TIMES[0])
233234
run_drain(data_dir)
@@ -243,6 +244,22 @@ def assert_community_promotion(data_dir: Path) -> None:
243244
run_drain(data_dir)
244245
assert reports_for(data_dir, legacy_number) == 3, "two UTC days did not promote bucketless reports"
245246

247+
write_report(data_dir, "legacy_bucket_first.json", legacy_number, BUCKETS[0], f"{TODAY}T00:01:00+00:00")
248+
run_drain(data_dir)
249+
assert reports_for(data_dir, legacy_number) == 0, "one bucket left a bucketless promotion shipped"
250+
write_report(data_dir, "legacy_bucket_second.json", legacy_number, BUCKETS[1], f"{TODAY}T00:02:00+00:00")
251+
run_drain(data_dir)
252+
assert reports_for(data_dir, legacy_number) == 0, "two buckets restored a demoted row"
253+
write_report(data_dir, "legacy_bucket_third.json", legacy_number, BUCKETS[2], f"{TODAY}T00:03:00+00:00")
254+
run_drain(data_dir)
255+
assert reports_for(data_dir, legacy_number) == 6, "three buckets did not restore a demoted row"
256+
257+
write_report(data_dir, "mixed_batch_first.json", mixed_batch_number, None, TIMES[0])
258+
write_report(data_dir, "mixed_batch_second.json", mixed_batch_number, None, f"{TODAY}T00:00:00+00:00")
259+
write_report(data_dir, "mixed_batch_bucket.json", mixed_batch_number, BUCKETS[0], f"{TODAY}T00:02:00+00:00")
260+
run_drain(data_dir)
261+
assert reports_for(data_dir, mixed_batch_number) == 0, "a mixed same-drain batch shipped with one bucket"
262+
246263
write_report(data_dir, "bucket_first.json", bucket_number, BUCKETS[0], TIMES[0])
247264
run_drain(data_dir)
248265
write_report(data_dir, "bucket_repeat.json", bucket_number, BUCKETS[0], TIMES[2])
@@ -264,7 +281,7 @@ def assert_community_promotion(data_dir: Path) -> None:
264281
write_json(data_dir / "spam_numbers.json", database)
265282
run_script("merge_community_reports.py", data_dir)
266283
assert reports_for(data_dir, old_number) == 0, "a legacy single-report row stayed in the shipped database"
267-
assert reports_for(data_dir, legacy_number) == 3, "a promoted row was demoted on the next merge"
284+
assert reports_for(data_dir, legacy_number) == 6, "a promoted row was demoted on the next merge"
268285

269286
pending_path = data_dir / "community_pending.json"
270287
pending = json.loads(pending_path.read_text(encoding="utf-8"))

0 commit comments

Comments
 (0)