Read the story

Architecture

Mirrors Denticon for two other apps.Refreshed every ten minutes.

A sync service pulls Denticon into a Firestore mirror, woken by webhooks, a worker every ten minutes and a nightly sweep. The estimator and the capacity planner read that mirror through a small registry API, each with its own bearer secret, and neither of them ever touches Denticon.

2.6 min

how far the mirror trailed Denticon on Sep 21 once the sync ran on a timer, down from 6.0 days

1

service holds Denticon credentials. Each caller holds only its own bearer

14 days

from the first Denticon commit on Sep 15 to reading the live practice on Sep 29

20,111

charts and 22,688 appointments in the first production backfill

New York --:-- simulated

Next slice run
CDF Registry API A sync service pulls Denticon into a Firestore mirror, woken by webhooks, a worker every ten minutes and a nightly sweep. The estimator and the capacity planner read that mirror through a small registry API, each with its own bearer secret, and neither of them ever touches Denticon. Cloud Scheduler nightly, 10 min, hourly bearer Treatment estimator one day of the book Denticon API Planet DDS, read token Denticon sync Cloud Run, 1 warm Firestore mirror charts, book, chairs Registry API one bearer per caller Capacity planner changes every minute Secret Manager Denticon keys, sync only Freshness check 12 cursors judged CDF dashboard the day's patient list API, webhooks POST sync client secret upsert by id read only changed rows day's book day list cursors Google Cloud, us-central1

Hover a part to trace what it touches. Click it for its notes.

  • Built by me
  • Managed by Google
  • Outside system
  • Main data path
  • On demand
  • Credentials cross

When it breaks

What each failure does, and what it does not do.

  1. A webhook never arrives

    The sweep at twenty past each hour lists slices whatever the receiver did, and the 4 AM sweep polls everything changed. A lost webhook delays data; it does not lose it.

    docs/runbook-denticon-cutover.md:392

  2. Denticon rate limits us

    429s and 5xx retry four times from a 500 ms base, capped at 30 seconds, with jitter. Four instances at most, so we never outrun the vendor.

    lib/denticon/paging.ts:441

  3. The mirror falls behind

    Every registry answer carries each office's cursor health, judged the same way as the freshness check, so a caller can show old data as old.

    lib/registry/shared.ts:62

What I tuned

Small choices that keep it fast and cheap.

  1. Webhooks first, poll as net

    Webhooks carry the load and a nightly poll is the safety net. I decided against a one minute poller against Denticon.

    docs/HANDOFF.md:232

  2. Changes, not whole months

    The planner asks for rows written since its watermark, minus an overlap longer than the ten minute sync lease. A window read is capped at 31 days.

    lib/registry/README.md

  3. One warm instance

    Minimum one, so Denticon's ten second webhook check never meets a cold start. Maximum four, so the sync stays under the vendor's rate limits.

    infra/cloudbuild-sync.yaml:168

Guards

Rules the code enforces, not rules people remember.

  1. One holder of the keys

    Only this repo holds Denticon credentials, and only the sync service mounts them. No other repo gets its own, not even as a fallback.

    docs/HANDOFF.md:225

  2. A secret per caller

    The estimator and the planner each have their own bearer, compared in constant time, trimmed, and closed when unset. Revoking one does not blind the other.

    lib/registry/auth.ts:36

  3. Proven filters only

    A query parameter not on the proven list throws before the request leaves. Proven means totalCount changed, not that the call returned 200.

    lib/denticon/paging.ts:121