{{ message }}
feat(api): move change tracking bookkeeping to Bigtable behind a backend flag - #4475
Draft
mogery wants to merge 4 commits into
Draft
feat(api): move change tracking bookkeeping to Bigtable behind a backend flag#4475mogery wants to merge 4 commits into
mogery wants to merge 4 commits into
Conversation
mogery
commented
Sep 1, 2026
mogery
commented
Sep 1, 2026
…end flag Replaces the append-only change_tracking_scrapes insert (30% of DB CPU) with a latest-wins Bigtable store keyed by team_id || sha256(url) || sha256(tag), one row per unique (team, url, tag). Cell timestamp = date_added with max_versions=1 GC, so reads return the newest scrape and out-of-order writes are shadowed. Content still lives in GCS. Rollout via CHANGE_TRACKING_BACKEND=postgres (default, unchanged) | dual (write both, read PG) | bigtable (write+read Bigtable). Includes a batched, resumable PG->Bigtable backfill script and emulator-backed store snips run in CI.
Change tracking bookkeeping now writes to and reads from Bigtable unconditionally -- no rollout flag, no dual-write, no Postgres fallback. Unconfigured BIGTABLE_* vars make the store throw, which lands in the existing non-fatal warn/warning paths (log_job insert warn, deriveDiff "Comparing failed" warning -- the same degraded posture as an unconfigured Postgres before). Removes the now-unused change_tracking_insert_scrape / diff_get_last_scrape_v7 RPC wrappers and fixes the emulator image reference (the dedicated emulator image does not exist; use cloud-sdk:slim + the small emulator package).
- Drop the Bigtable suffix from the store functions; it's the change tracking store, backend is an implementation detail - Remove table/family auto-provisioning from production code; the table is created out-of-band (instructions in the PR description) - Gate the store snips with TEST_PRODUCTION like the other production snips; drop the custom emulator detection and the env-defaulting helper file - Remove the Bigtable emulator from the self-host CI job, docker compose, and SELF_HOST.md -- change tracking stays unavailable in the self-hosting path - Restore the original insert log/warn strings - Drop the pointless `undefined | null` union; the read returns `ChangeTrackingLastScrape | null` and downstream uses it directly - Remove the backfill script - Fix GHSA-8988-4f7v-96qf (@opentelemetry/core <2.8.0, pulled via the bigtable client's OTel 1.x metrics chain): override the OTel trio + monitoring exporter to the 2.x line and disable the client's metrics handler, which is written against OTel 1.x APIs we don't use
mogery
force-pushed
the
change-tracking-bigtable
branch
from
September 1, 2026 13:30
05ab23d to
9d11aa3
Compare
The rebase resolved the pnpm-lock.yaml conflict against main's side and the regeneration run that followed never landed (amend executed from the wrong worktree), leaving the lockfile's overrides section stale relative to pnpm-workspace.yaml -- frozen installs failed with ERR_PNPM_LOCKFILE_CONFIG_MISMATCH.
This was referenced Sep 1, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
Rebuilds the change tracking pointer bookkeeping (
change_tracking_insert_scrape/diff_get_last_scrape_v7) on GCP Bigtable instead of Postgres. The PG table is pure "latest value per key" bookkeeping — content lives in GCS — so one row per scrape is overkill; the insert path is ~30% of database CPU time. The PG path is removed: Bigtable is the store, no flag.Design:
team_id ‖ sha256(url) ‖ sha256(tag), raw bytes, full 32-byte digeststag=null, so the high-entropy component occupies the first hashed position0x00vs0x01‖tag) preserves PG's null-vs-empty-tag distinctionm:job= job_id, cell timestamp =date_added(µs), column family GCmax_versions=1BIGTABLE_*vars make the store throw, landing in the existing non-fatal paths: the insert warns and continues, deriveDiff sets the "Comparing failed" warning — the same degraded posture self-host had before (change tracking remains unavailable in the self-hosting path)Out-of-band table creation
The app never provisions infrastructure. Before deploying:
gcloud bigtable instances create firecrawl-prod \ --cluster=firecrawl-prod-c1 --cluster-location=us-east1 \ --nodes=3 --storage=ssd gcloud bigtable instances tables create change_tracking \ --instance=firecrawl-prod \ --column-families="m:maxversions=1"(Equivalently:
cbt -instance=firecrawl-prod createtable change_tracking families=m:maxversions=1—familiesis a positional argument and the GC knob ismaxversions, no underscore.)Env:
BIGTABLE_PROJECT_ID,BIGTABLE_INSTANCE_ID,BIGTABLE_APP_PROFILE_ID(optional),BIGTABLE_CHANGE_TRACKING_TABLE(optional, defaultchange_tracking).Data migration from the PG table is a one-off ops task run alongside the deploy (
distinct on (team_id, url, change_tracking_tag) ... order by date_added desc→ bulkmutatewith cell timestamp =date_added; idempotent, latest-wins makes re-runs safe). After cutover, dropchange_tracking_scrapesand its RPCs server-side — that's the 30% CPU recovery.Tests
TEST_PRODUCTION-gated like the other production snips): write/read round-trip incl.date_addedfidelity, newest-wins, regressed-write-shadowed, null/empty/named tag partitioning, team isolation, missing row → null, key layout invariantsnew/previousScrapeAt: null; second scrape reportspreviousScrapeAt+changed|same— verified locally through the harness (server write → read → GCS fetch all exercised, 8/8)Dependency notes
@opentelemetry/core < 2.8.0) ships through the bigtable client's OTel 1.x metrics chain; the advisory is only patched on 2.x. The overrides move the tree's OTel trio + Google's monitoring exporter to 2.x, and the client constructs withmetricsEnabled: falsesince its metrics handler is written against OTel 1.x APIs we don't consume. Audit is clean of any new findings (the remaining ip-address/decode-uri-component advisories pre-date this PR and ip-address is allowlisted inaudit-ci.jsonc).Number(), nevertypeof === "number".