Read the story

Architecture

Mirrors Denticon for every app.Webhooks first, nightly poll behind.

When a record changes, Denticon posts a pointer; the receiver saves it and answers inside ten seconds, and a worker every ten minutes fetches the slice with the only Denticon credentials we hold. A 4 AM sweep polls everything changed since each office's cursor, which catches any webhook that went missing.

2.6 min

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

0 bytes

of a webhook body kept as data. It is a pointer, and the worker fetches the record itself

6 days

from the first Denticon commit on Sep 15 to the sync service live on Sep 21

4 services

read the mirror: the CDF, the estimator and the planner through the registry, and the report renderer

New York --:-- simulated

Next slice run
Denticon Sync When a record changes, Denticon posts a pointer; the receiver saves it and answers inside ten seconds, and a worker every ten minutes fetches the slice with the only Denticon credentials we hold. A 4 AM sweep polls everything changed since each office's cursor, which catches any webhook that went missing. Denticon API read token, 60 min CDF registry estimator, planner Denticon webhooks 3 offices, 6 record types Webhook receiver saves a pointer, 200 Slice queue Firestore, 3 tries Sync worker holds the Denticon keys Firestore mirror upsert by record id Cloud Scheduler 10 min, :20, 4 AM bearer Freshness check missing is worst pointer save claim upsert fetch, poll wake read only cursors

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 every slice the vendor holds, whatever the receiver saw, and the 4 AM poll reads everything changed. Slices are kept seven days, so there is time.

    lib/denticon/subscriptions.ts:401

  2. Denticon rate limits us

    Four attempts from a 500 ms base, capped at 30 seconds, with jitter, and four instances at most so a burst cannot outrun the vendor.

    lib/denticon/paging.ts:440

  3. A slice keeps failing

    Three attempts, then it stays failed with a short error slug for a person. A slice that failed three times will not start working, and retrying it starves the ones that would.

    lib/denticon/subscriptions.ts:290

What I tuned

Small choices that keep it fast and cheap.

  1. Answer first, work later

    The receiver only saves a pointer. Fetching, mapping and writing happen in the worker, far from Denticon's ten second reply window.

    app/api/denticon/slices/[secret]/route.ts:16

  2. One warm instance

    Minimum one, so a webhook check never meets a cold start. Maximum four, because a worker is not a request server and the vendor's limits are undocumented.

    infra/cloudbuild-sync.yaml:168

  3. One image, never rebuilt

    The sync service deploys an image the dashboard pipeline already built. Both run provably the same container, and the credentials decide which routes can work.

    infra/cloudbuild-sync.yaml:46

Guards

Rules the code enforces, not rules people remember.

  1. Keys on one service

    The Denticon credentials are mounted on this service only. The dashboard has none, so its copies of these routes cannot reach the vendor even if called.

    infra/cloudbuild-sync.yaml:52

  2. A webhook is a hint

    Nothing in a notification is stored as data or picks a code path. The worst a bad one can do is one extra authenticated fetch of a slice that is ours.

    app/api/denticon/slices/[secret]/route.ts:39

  3. Deletes leave a mark

    A Delete slice marks the mirror document deleted upstream. A chart that vanished while another record points at it would be worse than one that says what happened.

    lib/denticon/slice-worker.ts:31