← Back
Home › Systems › Observability and operations

synthetic-monitoring

Runs scripted checks against a service from probers in several locations on a fixed schedule, to tell operators whether it works as a user would experience it.

Checkly, Pingdom

Open the synthetic-monitoring pagesynthetic-monitoring Browser automation protocolagentic-ai: CDP / WebDriver BiDi commands and events over websockets to drive browsersHdr histogramdata-structures: Log-linear bucketed histograms with bounded relative error for latency percentilesQuantile sketchesdata-structures: Mergeable quantile sketches: t-digest, DDSketch, HDR histogramsDeterministic simulationtestingdistributed: Run the whole system in one thread under a seeded scheduler with simulated network, disk and clock, so every failure replays from its seed.›Leasesdistributed: A lock that expires on its own: bounded-time ownership that survives crashes and partitions, given bounded clock drift and fencing tokens on writes.›Primary-backup replicationdistributed: One node orders all writes and ships them to standbys; the acknowledgement rule (none, one, all) sets what a crash loses, and an external arbiter decides who takes over.›Rendezvous hashingdistributed: Score every node with hash(key, node) and pick the highest: minimal key movement and exact weights without a ring, at O(n) per lookup.›Process supervisionhardware-os: A parent process starts services, reaps their exits and restarts them under a delay and burst-limit policy, then stops them with SIGTERM before SIGKILL.›DNS message format and clientlookupsnetworking: Binary query/response format of DNS (RFC 1035) and how a stub client uses it: record types, name compression, UDP/TCP/EDNS sizes, TTL caching.›Coverage-guided fuzzingsecurity: Mutate inputs, run an instrumented target, and keep any input that reaches a new code edge; feedback turns blind guessing into incremental search.›Mutual TLSsecurity: TLS where the client also proves a certificate key, so every connection carries a verified, rotatable machine identity.›TLS 1.3security: The standard encrypted, authenticated transport; one round trip, only forward-secret AEAD suites, and little left to misconfigure.›X509 certificatessecurity: Certificate chains, path validation, SANs, revocation and short-lived issuancePoint-in-time recoverystorage: Periodic base backups plus a continuously archived change log let you rebuild the state as of any instant, then branch from there.›Alert groupingsystems: Group, deduplicate, inhibit and silence alerts by label sets before notifyingCron schedulingsystems: Cron expression parsing and next-fire-time computation, with catch-up and time-zone rulesGraceful drainingsystems: On a termination signal, stop taking new work, finish in-flight work until a deadline, tell clients to reconnect elsewhere, then exit; clients resume from a token.›Health checkingsystems: Decide per backend whether to route to it using probes and real-traffic errors, with consecutive-count hysteresis, capped ejection and a panic fallback.›Hierarchical timing wheelsystems: Timers bucketed by expiry into wheels of slots, coarser wheels for far deadlines: O(1) insert and cancel for millions of timeouts.›Cooperative schedulingconcurrency: Green threads/coroutines yielding at await points on a user-space schedulerFault injectiondistributed: Inject latency, errors, kills and partitions via proxies, netem or hooksRecord replaydistributed: Record every nondeterministic input (time, randomness, I/O, model and tool outputs, per-tick player input) and re-execute deterministically for debugging, replays and state rebuildFencing tokensdistributed: A number that grows with every lock grant and is checked by the protected resource, so a paused ex-holder's late write is rejected.›PRNGs (PCG, xoshiro)numerics: Small, fast, statistically strong non-cryptographic generators: PCG32 and xoshiro256**, with seeding, parallel streams and unbiased ranges.›Code coverage instrumentationcompilers: Insert counters into compiled code to record which blocks, edges or lines ran; a spanning tree of the control-flow graph cuts the counters needed.›Delta debuggingcompilers: Ddmin minimization of failing inputs or change setsFast hash functionsdata-structures: Non-cryptographic hashes built from 64×64→128-bit multiplies and xor-shifts; pick by key size, trust level and whether the hash is persisted.›Sanitizerssecurity: ASan/UBSan/TSan shadow memory and instrumentation to catch memory bugsWrite-ahead logstorage: Append each change to a checksummed sequential log and fsync it before touching data pages; the basis of crash durability in nearly every database.›AEAD (AES-GCM,ChaCha20-Poly1305)security: Authenticated encryption that encrypts and detects tampering in one call; the only kind of symmetric encryption you should use.›Cryptographic hashes (SHA-2,BLAKE3)security: Collision-resistant fingerprints of data; SHA-256 for interoperability, BLAKE3 for speed, HMAC/HKDF for keyed uses.›+ 8 moreTransparent interceptionnetworking: Redirect traffic to a local proxy (iptables TPROXY/REDIRECT, eBPF) without app changesSyscall interceptionsecurity: Intercept syscalls via ptrace, seccomp user notify or a user-space kernelControl-flow graphcompilers: Split a linear instruction list into basic blocks and connect them by jumps; the graph every optimizer, SSA builder and dataflow pass walks.›Minimum spanning treegraphs: Cheapest set of edges connecting all vertices; Kruskal sorts edges and joins components with union-find, Prim grows one tree, Boruvka merges in parallel rounds.›Failure bisectionsearch-sort: Binary search over commits, batches or inputs to isolate a failureCRC32C checksumshardware-os: The Castagnoli CRC-32 with dedicated instructions on x86 (SSE4.2) and ARMv8. It is the standard fast integrity check for storage blocks, logs and network frames.›fsync, O_DSYNC and O_DIRECThardware-os: What fsync, fdatasync, O_DSYNC, O_DIRECT and F_FULLFSYNC really guarantee on Linux and macOS, and the exact call sequences for crash-safe files.›Constant time codesecurity: Writing secret-dependent code without branches or memory lookups on secrets; constant-time compare and selectCsprngsecurity: OS-backed cryptographically secure random numbers (getrandom), and when a fast PRNG is not enough+ 1 moreEbpfhardware-os: Verified in-kernel programs on hooks (kprobes, XDP, tc) with mapsPtrace process controlhardware-os: Ptrace: attach, read and write registers and memory, single-step, intercept signalsSeccomp bpfsecurity: Syscall filtering with seccomp BPF programsBFS and DFSgraphs: The two basic graph traversals; BFS gives hop distances and levels, DFS gives discovery/finish order for cycles, components and topological order.›d-ary heapdata-structures: Array heap with d children per node (d = 4 typical): half the binary heap's depth, siblings in one cache line, faster priority queues.›Union-findgraphs: Disjoint-set forest with union by size and path compression; merges sets and answers same-set queries in effectively constant time.›SIMD programminghardware-os: Process 4-16 lanes per instruction with AVX2, AVX-512 or NEON via Rust std::arch, with runtime dispatch and a portable fallback.›+ 2 moreSparse matrices (CSR)numerics: Compressed sparse row stores only nonzeros as row pointers, column indices and values; SpMV streams them once, so it is memory-bandwidth bound.›

How it is built

Architecture overview

Synthetic monitoring answers one question from the outside: does the service work right now, as a user would experience it? A fleet of probers in several locations runs scripted checks against a target on a fixed schedule: resolve a name, open a TLS connection, fetch a URL, or drive a real browser through a login. Every run produces a result (success flag, timings, certificate facts). A central side turns the stream of results into availability, latency percentiles and alerts. In the vocabulary of the Google SRE book this is black-box monitoring: symptom-oriented, it reports problems that are active now, not ones predicted from internal state.

The idea that makes it work is that a single failed probe proves little. A check is declared down only after retries and agreement between locations, and the checks themselves are spread over locations by a deterministic hash so that losing a prober moves only that prober's checks.

Parts:

 check definitions ──► scheduler (timing wheel, jitter)
                           │ due (check, run-id)
                           ▼
              assignment: top-k locations by rendezvous hash
                           │
        ┌──────────────────┼──────────────────┐
        ▼                  ▼                  ▼
   prober eu-1        prober us-1        prober ap-1      (each holds a lease on its work)
   DNS/TCP/TLS/HTTP   same                same
   browser pool       same                same
        │ result: ok, phase timings, cert expiry, error class
        └──────────────────┬──────────────────┘
                           ▼
        result ingest ──► sketches per (check, location, window) ──► dashboards, SLO views
                           │
                           ▼
              health evaluator (retries, quorum) ──► alert grouper ──► pager / chat / webhook

There are two processes with different needs. The probers are many, small, and placed where users are; they must be cheap to run and must not need to be trusted with the whole fleet's data. The control side is few, holds the check definitions and the long-term results, and must be more reliable than anything it watches.

How the parts fit together

Check scheduler

A check is a recurring job, so its schedule is a cron-scheduling (no page yet) style or fixed-interval rule. With many thousands of checks, a naive loop that scans all of them every second is wasteful; a Hierarchical timing wheel buckets deadlines into slots so that inserting, cancelling and firing a timer are constant-time operations, and the scheduler only touches checks that are due. Two details matter for correctness more than for speed:

  • Jitter. Checks that share an interval must not all fire on the same second, or the monitor itself becomes a load spike on the target (and on its own result path). The first fire is offset by a stable per-check value, for example derived from a hash of the check id, so a restart of the scheduler does not re-align everything.
  • Missed runs are skipped, not replayed. After an outage of the scheduler, running every missed probe at once would measure the recovery storm. The scheduler computes the next due time from now.

Location assignment

Each check names how many locations should run it. The assignment function is Rendezvous hashing: for a check, score every live location by hashing the pair (check id, location id) and take the top k. It needs no table and no coordination, every control-plane node computes the same answer, and when a location leaves only the checks that had it in their top k move, to their next best location. A ring with virtual nodes would give a similar property; rendezvous hashing is simpler here because the location set is tiny (tens, not thousands) so scoring all of them per check is cheap.

Moving checks quickly after a failure needs an agreed view of who is alive. A prober holds a Lease on its assignment, renewed with each heartbeat; when the lease expires the control side stops counting its results and the hash simply recomputes without it. The same lease pattern protects the control side: one evaluator is leader for a check shard at a time. A duplicate probe run during a hand-over is harmless (probes are side-effect-free by design) so the system accepts at-least-once execution instead of paying for exactly-once. Browser journeys that write data, such as placing a test order, are the exception: they need their own idempotency and cleanup.

Protocol probes

These are the cheap, frequent checks, and each one splits a request into phases so a failure is attributed to a layer, not just "down".

  • Name resolution (DNS protocol, RFC 1035): query a record type against a chosen resolver or directly against the authoritative servers, compare the answer with the expected value, and record query time. Probing the authoritative servers separately from a recursive resolver separates "our zone is broken" from "a resolver cache is stale".
  • TLS (TLS 1.3, RFC 8446): connect, complete the handshake, and record handshake time, negotiated version and cipher suite. Failing to complete the handshake is a different alert from a slow one.
  • Certificates (x509-certificates, no page yet; RFC 5280): validate the chain against a trust store, check the hostname against the subject alternative names, and export days-to-expiry as a metric. The most useful alert in this whole system is often "certificate expires in N days", because it is a failure that is certain, dated and completely avoidable.
  • HTTP and TCP/ICMP: status code, redirect chain, body assertions, time to first byte. The Prometheus blackbox exporter exposes HTTP, HTTPS, DNS, TCP, ICMP and gRPC probes; its ICMP prober needs elevated privileges on the host, which is a deployment constraint for prober images.

Each probe has a hard timeout shorter than the check interval, and its result carries an error class (DNS failure, connect timeout, TLS failure, bad status, assertion failed) as a bounded label, never the raw error string, so the number of distinct series stays fixed.

Browser probes

Some failures only appear in a rendered page: a script that throws, a login form that no longer submits. A browser probe runs a script against a real browser engine through a remote-control protocol such as the Chrome DevTools Protocol (browser-automation-protocol, no page yet), which is JSON messages over a WebSocket. Playwright wraps this control for Chromium, Firefox and WebKit behind one API. Contracts to get right:

  • Isolation. A fresh browser context per run, so cookies and storage never leak between runs.
  • Budget. A journey has a wall-clock limit and a step limit; a hung page must not pin a prober.
  • Evidence. On failure, keep a screenshot, the failing step and the network log, because an on-call engineer will otherwise have only "step 4 timed out".
  • Cost. A browser run is orders of magnitude heavier than a DNS query, so browser checks run at lower frequency from fewer locations, in their own worker pool.

Result aggregation

Availability is a ratio of counts, which merges by addition. Latency is not: you cannot average the p95 of three locations to get the p95 of the union. The probers therefore send timings into mergeable summaries, quantile-sketches (no page yet), such as DDSketch, which has relative-error guarantees and merges exactly, or the t-digest. A sketch per (check, location, window) is small and fixed in size, so a one-minute window from every location can be combined into an hourly or daily one without keeping raw samples. Reporting by location also reveals regional problems that a global number hides.

Inside a prober, hdr-histogram (no page yet) is the fixed-size bucketed form for the timings it measures itself: log-linear buckets with bounded relative error give DNS, connect, TLS and first-byte percentiles for a window from a few kilobytes, and two histograms with the same layout merge by adding bucket counts. That is what is sent to the aggregator, which merges across locations and windows as above.

Health evaluator

The evaluator applies Health checking semantics to the result stream: a check flips to down after N consecutive failures (or when a quorum of its locations fails within a window) and back to up after M successes. Asymmetric thresholds avoid flapping. Two rules keep the signal honest: a failure seen from only one location, with the other locations healthy, is classified as a location problem or a regional path problem, not a service outage; and a prober that stops reporting is a no data state, never up.

Alerting

State changes feed an alert grouper (alert-grouping, no page yet). Alerts carry label sets (check, location, error class, team); a map keyed by the label set (Swiss table) deduplicates repeats, and timers on the timing wheel implement "wait a few seconds to collect related alerts, then send one notification, and remind every few hours". Prometheus Alertmanager is the reference for this: it deduplicates, groups, silences and inhibits alerts and routes them to receivers such as e-mail, PagerDuty or a webhook. Inhibition matters here: a DNS failure should mute the HTTP and TLS alerts for the same target, since they are consequences.

Operations and assurance

A check that stops running looks like a healthy target, so the monitoring system must watch itself first. Each prober runs under Process supervision, which restarts a crashed process with a delay and a burst limit; a prober that cannot stay up stops renewing its leases, and the location assignment moves its checks elsewhere. When a location is retired or updated, Graceful draining is the clean exit: it stops accepting new runs, lets running probes and browser journeys finish by a deadline, sends the last results, and releases its leases so the checks move at once, without a gap in the series.

Probers sit in many networks, some inside customers' private networks, and report to a central service. Mutual TLS gives each prober a verified, rotatable identity on that link: the control plane hands check definitions (which may hold credentials for login journeys) only to probers it enrolled, and accepts results only from them, so a rogue host cannot feed false "all green" data. The control plane's state (check definitions, current up/down state, flap counters) is protected by Primary-backup replication: one node orders the writes and ships them to a standby, the acknowledgement rule decides whether a state change that was alerted on can be lost, and an external arbiter chooses the primary so two schedulers never run the same checks as owners. Point-in-time recovery rebuilds check definitions and result history to the instant before a bad bulk edit (an interval changed to a day, checks deleted) from a base backup plus the archived change log.

Probers parse answers from servers they do not control: DNS messages, TLS certificates, HTTP responses and the pages loaded by the browser. These parsers are the inputs for Coverage-guided fuzzing, where a mutated response that reaches a new edge is kept; a crash in a prober on one hostile response would blind that location. Deterministic simulation testing runs scheduler, location assignment, probers and a simulated network and clock in one thread under a seeded scheduler, with locations that disappear, leases that expire and clocks that skew, and checks that every check runs at its interval from some location and that flap control does not alert on a single lost probe.

Data and control flow

One HTTP check, end to end

  1. The timing wheel fires check C at its jittered due time; the scheduler writes a run record (check id, run id, due time).
  2. The assignment function scores the live locations for C and picks the top k; each chosen prober receives the run (push) or picks it up on its next poll (pull).
  3. The prober resolves the name, connects, performs the TLS handshake, sends the request and applies the assertions, recording each phase's duration; a global timeout cuts the run off.
  4. The prober sends one result message: success flag, error class, phase timings, certificate not-after time.
  5. Ingest adds the timings to the sketch for (C, location, current window) and the flag to a counter; the evaluator updates C's state.
  6. If the state changes, an alert event with C's labels goes to the grouper. Time is dominated by the target and the network (tens to hundreds of milliseconds for a healthy HTTPS fetch, order of); the monitor's own overhead should be negligible against it.

A prober fails

  1. Its lease stops being renewed; the lease expires after its term.
  2. The control side drops that location from the live set; rendezvous hashing recomputes top k for the affected checks only.
  3. Replacement probers pick up those checks on their next cycle; results from the failed prober that arrive late are discarded by lease epoch or run id.
  4. A "prober missing" alert is raised on the monitor itself, routed by a path that does not depend on the failed prober.

An alert

  1. The evaluator emits down for C with labels. 2. The grouper waits for related alerts in the same group, then sends one notification. 3. A repeat timer re-notifies until the check recovers or someone silences it.

Scaling

In the order they bite:

  1. Browser probes cost most. Levers: lower frequency, fewer locations, a separate pool with a cap on concurrent browsers, and recycling browser processes after a bounded number of runs.
  2. Timer and scheduling load. Millions of timers are cheap on a timing wheel; if one scheduler is not enough, shard checks by hashing the check id to a scheduler shard.
  3. Result volume. Checks × locations × runs per minute. Levers: pre-aggregate in the prober into one summary per window instead of one message per run, keep raw runs short-lived, store sketches long-lived.
  4. Cardinality. Labels such as the raw URL or error text explode series counts; bound them to fixed classes.
  5. Prober fleet. Add locations; rendezvous hashing rebalances only a share of checks proportional to the change.
  6. Alert volume. Grouping and inhibition keep human notifications flat while checks grow.

Failure modes

FailureSymptomCauseFix
False outageone location reports down, users are fineprober-side network problem or noisy pathquorum across locations, retries before state change, label by location
Monitor blind spoteverything looks green during a real outageprobe hits a cache, a CDN edge or a health endpoint that skips the real dependencyassert on content, use cache-busting where legitimate, add a deep check through the real path
Stale DNS answercheck passes against an address that users no longer getresolver cache between prober and authoritative serversprobe authoritative servers too, record TTL
Expired certificatesudden total TLS failureno one watched the dateexport days-to-expiry, alert at several lead times
Thundering herdtarget shows a periodic load spikeall checks share a start offsetper-check jitter, no restart re-alignment
Duplicate probingdouble load or double side effects after hand-overlease overlap, at-least-once runsidempotent probes, lease epochs on results
Alert stormpager floods during an outageone alert per check per locationgrouping by label, inhibition, longer first-wait
Flappingup/down toggles every few minutessingle-failure thresholds, borderline latencyN-of-M rule, hysteresis between down and up
Monitor downno alerts at all, which looks like healththe monitor shares infrastructure with the targetexternal dead-man switch, a second independent monitor, no data is not up
Brittle browser scriptjourneys fail after a UI changeselectors tied to layoutstable test ids, screenshots, owner per script

Real implementations

namewhat it isrepolicencenotes
Prometheus blackbox exporterprobes HTTP, HTTPS, DNS, TCP, ICMP and gRPC targets and exposes the results as metricsprometheus/blackbox_exporterApache-2.0the reference for protocol probes; scheduling and alerting come from Prometheus and Alertmanager
Grafana synthetic monitoring agentthe prober that runs checks on behalf of a hosted synthetic monitoring servicegrafana/synthetic-monitoring-agentApache-2.0topics on the repo list blackbox-exporter and k6
Uptime Kumaself-hosted uptime monitor with a web UIlouislam/uptime-kumaMITsingle-node: scheduler, prober and UI in one process
Playwrightbrowser automation for Chromium, Firefox and WebKit through one APImicrosoft/playwrightApache-2.0the usual engine for scripted browser checks
Grafana k6scriptable load and check runner in Go with JavaScript scriptsgrafana/k6AGPL-3.0scripted HTTP checks
Alertmanagerdeduplicates, groups, silences, inhibits and routes alertsprometheus/alertmanagerApache-2.0the alerting half of the design

The alternative: white-box monitoring, where the service exports its own metrics and a collector scrapes them, sees causes that probes cannot (queue depth, error counts by handler) but cannot see failures that sit in front of the service: DNS, certificates, routing, a CDN. The Google SRE book recommends heavy use of white-box monitoring with modest but critical use of black-box monitoring; synthetic monitoring is that second half.

Sources

  • B. Beyer et al., Site Reliability Engineering, ch. 6 "Monitoring Distributed Systems" (black-box versus white-box monitoring). chapter
  • RFC 8446, The Transport Layer Security (TLS) Protocol Version 1.3. rfc
  • RFC 5280, Internet X.509 Public Key Infrastructure Certificate and CRL Profile. rfc
  • RFC 1035, Domain names - implementation and specification. rfc
  • C. Masson, J. E. Rim, H. K. Lee, DDSketch: A fast and fully-mergeable quantile sketch with relative-error guarantees, 2019. paper
  • T. Dunning, O. Ertl, Computing Extremely Accurate Quantiles Using t-Digests, 2019. paper
  • Chrome DevTools Protocol. docs
  • Prometheus blackbox exporter README (supported probers, ICMP privileges). repo

Similar systems

Share the most building blocks with this one.