Skip to content

Commit 54ba28c

Browse files
committed
fix: shard oversized topic scan by timestamp
1 parent a0bc562 commit 54ba28c

2 files changed

Lines changed: 44 additions & 11 deletions

File tree

scripts/sync_topic_plugins.py

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -312,21 +312,28 @@ def collect_query(self, query_text: str, first: dict[str, Any]) -> list[dict[str
312312
f"search shard did not converge: latest={latest_total}, unique={len(seen)}"
313313
)
314314

315-
def collect_date_range(
315+
def collect_time_range(
316316
self,
317-
start: dt.date,
318-
end: dt.date,
317+
start: dt.datetime,
318+
end: dt.datetime,
319319
) -> list[dict[str, Any]]:
320-
query_text = f"topic:{TOPIC} created:{start.isoformat()}..{end.isoformat()}"
320+
def timestamp(value: dt.datetime) -> str:
321+
return value.astimezone(dt.timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
322+
323+
query_text = f"topic:{TOPIC} created:{timestamp(start)}..{timestamp(end)}"
321324
first = self.search_page(query_text, 1)
322325
total = int(first.get("total_count", 0))
323326
if total <= 1000:
324327
return self.collect_query(query_text, first)
325328
if start >= end:
326-
raise SyncError(f"more than 1,000 topic repositories were created on {start}")
327-
midpoint = start + (end - start) // 2
328-
return self.collect_date_range(start, midpoint) + self.collect_date_range(
329-
midpoint + dt.timedelta(days=1), end
329+
raise SyncError(
330+
f"more than 1,000 topic repositories were created at {timestamp(start)}"
331+
)
332+
midpoint = start + dt.timedelta(
333+
seconds=int((end - start).total_seconds()) // 2
334+
)
335+
return self.collect_time_range(start, midpoint) + self.collect_time_range(
336+
midpoint + dt.timedelta(seconds=1), end
330337
)
331338

332339
def search(self, max_pages: int | None = None) -> tuple[list[dict[str, Any]], int]:
@@ -344,9 +351,10 @@ def search(self, max_pages: int | None = None) -> tuple[list[dict[str, Any]], in
344351
if total <= 1000:
345352
return self.collect_query(base_query, first), total
346353

347-
today = dt.datetime.now(dt.timezone.utc).date()
348-
repositories = self.collect_date_range(dt.date(2008, 1, 1), today - dt.timedelta(days=1))
349-
repositories += self.collect_date_range(today, today)
354+
now = dt.datetime.now(dt.timezone.utc).replace(microsecond=0)
355+
repositories = self.collect_time_range(
356+
dt.datetime(2008, 1, 1, tzinfo=dt.timezone.utc), now
357+
)
350358
deduplicated = {int(item["id"]): item for item in repositories}
351359
if len(deduplicated) != len(repositories):
352360
raise SyncError("date-sharded topic search returned duplicate repository IDs")

tests/test_topic_sync.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,31 @@ def search_page(self, query_text, page):
413413
items = GrowingClient().collect_query("topic:dsh-plugin", first)
414414
self.assertEqual(len(items), 104)
415415

416+
def test_oversized_day_is_split_by_timestamp(self):
417+
class TimestampClient(topic_sync.GitHubClient):
418+
def __init__(self):
419+
self.queries = []
420+
421+
def search_page(self, query_text, page):
422+
self.queries.append(query_text)
423+
if "00:00:00Z..2026-08-14T23:59:59Z" in query_text:
424+
return {"total_count": 1500, "items": []}
425+
if "00:00:00Z..2026-08-14T11:59:59Z" in query_text:
426+
return {"total_count": 1, "items": [{"id": 1}]}
427+
if "12:00:00Z..2026-08-14T23:59:59Z" in query_text:
428+
return {"total_count": 1, "items": [{"id": 2}]}
429+
raise AssertionError(query_text)
430+
431+
client = TimestampClient()
432+
items = client.collect_time_range(
433+
topic_sync.dt.datetime(2026, 8, 14, tzinfo=topic_sync.dt.timezone.utc),
434+
topic_sync.dt.datetime(
435+
2026, 8, 14, 23, 59, 59, tzinfo=topic_sync.dt.timezone.utc
436+
),
437+
)
438+
self.assertEqual([item["id"] for item in items], [1, 2])
439+
self.assertEqual(len(client.queries), 3)
440+
416441
def test_changelog_and_report_are_idempotent(self):
417442
promoted = [
418443
{

0 commit comments

Comments
 (0)