Skip to content

improving lobbying data pipeline reliability - #2221

Open
nesanders wants to merge 1 commit into
codeforboston:mainfrom
nesanders:lobbying-pipeline-reliability
Open

improving lobbying data pipeline reliability#2221
nesanders wants to merge 1 commit into
codeforboston:mainfrom
nesanders:lobbying-pipeline-reliability

Conversation

@nesanders

Copy link
Copy Markdown
Collaborator

Summary

The weekly lobbying scraper's incremental cursor (scrapers/lobbying) stores which disclosure URLs it's already processed as two fields — a growing URL list and a per-registrant cache — on a single Firestore document. Firestore caps a document at 1MB. Run against the full corpus, that document grows past the limit partway through, and every write after that point fails silently: the scraper thinks it succeeded, but every registrant processed for the rest of the run (and beyond, since the cursor itself stops updating) never actually gets recorded as done. In practice this meant the weekly scraper silently stopped making progress once enough disclosures accumulated.

Fix

Moved the cursor to subcollections — one small document per URL — mirroring the pattern the backfill cursor (scrapers/lobbyingBackfill/processedUrls) already uses elsewhere in this same file. No document ever grows large enough to hit the limit, regardless of how much history accumulates.

Testing

  • tests/test_scrape.py (new): unit tests against a small in-memory Firestore fake, including a regression test asserting the parent cursor document stays small. Confirmed this actually catches the bug by checking out the pre-fix code and rerunning the suite against it (fails), then restoring the fix (passes).
  • Full existing suite (pytest tests/) still green — 49 tests total.
  • Validated live against a project with real accumulated data (--mode weekly --dry-run).

… limit

The live weekly cursor (scrapers/lobbying) stored the entire processed-URL
history and per-registrant summary cache as two fields on a single Firestore
document. Firestore documents have a hard 1MB size limit; once run against
the full corpus, this document grew past it partway through, and every write
after that point failed silently — meaning every registrant processed for
the rest of that run (and any future run building on that cursor) was
silently skipped rather than captured.

Moved to subcollections — scrapers/lobbying/processedUrls/{hash} and
scrapers/lobbying/summaryCache/{hash}, one small doc per URL — mirroring the
pattern the backfill cursor (scrapers/lobbyingBackfill/processedUrls)
already used elsewhere in this same file. Point lookups replace the
in-memory set/dict that used to get loaded and saved as one blob on every
write.

Tested with a small in-memory Firestore fake (tests/test_scrape.py) real
enough to simulate write-then-read-back across calls, including a
regression test asserting the parent cursor doc never grows beyond a
handful of small fields. Confirmed this test fails against the pre-fix code
by checking it out and rerunning the suite, then passes again with the fix
restored. Also validated live against a project with existing data.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@vercel

vercel Bot commented Aug 23, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
maple-dev Ready Ready Preview Aug 23, 2026 10:36pm

Request Review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant