Postmortem · Oct 21 – 25, 2026
Runaway connection churn filled the database disk and took the platform down
- Started
- 00:00 UTC
- Wednesday, Oct 21, 2026
- Ended
- 03:44 UTC
- Sunday, Oct 25, 2026
- Duration
- 99 h 44 min
- Healthy again in between, Oct 25, 01:27 – Oct 25, 02:30
- Worst
- Full outage
- 23 h 32 min in 3 stretches
On Oct 21 a provider-quality filter switched itself on at its default date and left almost no providers in two of our selection pools. Our hosted proxy service, trying to replace providers that failed, began creating short-lived connections far faster than the database could serve them, and for two days connections failed or stalled for many people. Every one of those connections also added to the database's storage. On Oct 23 a logging fault on the nearly full disk filled it completely, and the platform was down for 16 hours, then 6 more while we expanded the storage. That evening the same churn tied up payment reservations, and for about five hours apps could find providers but not send traffic.
TimelineAll times UTC
- Healthy
- Degraded
- Partial outage
- Major outage
- Full outage
Free space on the database disk: the readings
| Time (UTC) | Free space on the database disk |
|---|---|
| Oct 21, 00:00 | 390 GB |
| Oct 21, 13:00 | 334 GB |
| Oct 21, 15:00 | 306 GB |
| Oct 21, 23:57 | 145 GB |
| Oct 22, 00:00 | 345 GB (after an index rebuild and a nightly cleanup) |
| Oct 22, 05:50 | 249 GB |
| Oct 22, 06:02 | 467 GB (after old data was dropped) |
| Oct 23, 06:30 | about 200 GB (reconstructed afterwards) |
| Oct 23, 11:12 | 0 GB (full; the database stopped) |
| Oct 23, 20:05 | 129 GB (log files removed) |
| Oct 23, 21:44 | 360 GB (after a full vacuum) |
- Full outage23 h 32 min
- Major outage14 h 04 min
- Partial outage43 h 27 min
- Degraded17 h 38 min
Phases
Chapter 1A scheduled filter emptied provider pools and set off a connection storm
- Oct 21, 00:00 – Oct 21, 14:00Degraded14 hTwo selection pools were nearly empty; most people were not affected yet.
- Oct 21, 14:00 – Oct 21, 15:00Partial outage1 hDatabase queueing began and connections started to fail.
- Oct 21, 15:00 – Oct 22, 02:08Major outage11 h 08 minApps found providers but connections failed or stalled for many people.
- Oct 22, 02:08 – Oct 22, 13:45Partial outage11 h 37 minPacing and the postponed filter eased it, but the churn kept going on its own.
- Oct 22, 13:45 – Oct 22, 14:41Degraded56 minChurn brakes broke the loop.
- Oct 22, 14:41 – Oct 23, 11:12Partial outage20 h 31 minThe loop came back under daytime load, and connections stayed unreliable until the disk filled.
Chapter 2The database disk filled, then payment reservations jammed
- Oct 23, 11:12 – Oct 24, 03:20Full outage16 h 08 minThe database server's disk filled and the database stopped. The whole platform was down.
- Oct 24, 03:20 – Oct 24, 05:29Partial outage2 h 09 minServing again on most servers while the storage was expanded.
- Oct 24, 05:29 – Oct 24, 11:29Full outage6 hA second, planned full outage so the storage expansion could finish.
- Oct 24, 11:29 – Oct 24, 16:48Partial outage5 h 19 minBack with reduced capacity: six load-balancer groups served nothing after an operating-system upgrade renamed their network interfaces.
- Oct 24, 16:48 – Oct 24, 19:30Degraded2 h 42 minFull capacity was back while backlogs drained.
- Oct 24, 19:30 – Oct 24, 20:54Full outage1 h 24 minApps could find providers but could not send traffic.
- Oct 24, 20:54 – Oct 24, 21:30Partial outage36 minExtra data grants restored traffic for about 35 minutes.
- Oct 24, 21:30 – Oct 25, 00:26Major outage2 h 56 minThe grants filled up and failures returned until the fix was ready.
- Oct 25, 00:26 – Oct 25, 01:27Partial outage1 h 01 minThe fix rolled out in stages.
- Oct 25, 01:27 – Oct 25, 02:30Healthy1 h 03 minHealthy.
- Oct 25, 02:30 – Oct 25, 03:44Partial outage1 h 14 minSlow again: a long-running backup kept the database from cleaning up.
Key moments
- Oct 21, 00:00A provider-quality filter turns itself on at its default date. It was meant to wait for a decision that never got made.
- Oct 21, 14:00Database queueing starts as our hosted proxy service creates and discards connections hundreds of times a second.
- Oct 22, 02:08Discovery pacing ships to every proxy, and refused connection attempts fall by about 90%.
- Oct 22, 04:03The filter is postponed. The churn keeps going on its own.
- Oct 22, 13:45Churn brakes break the loop, until daytime load brings it back at 14:41.
- Oct 23, 11:12The database server's disk fills and the database stops.
- Oct 24, 05:29Every service stops so the storage expansion can finish. The platform is back at 11:29.
- Oct 24, 19:30Apps can find providers but not send traffic.
- Oct 25, 00:26The fix for the payment-reservation stall starts rolling out. Service is healthy at 01:27.
- Oct 25, 03:42A long-running backup is stopped, and the database cleans up within minutes.
Who was affected
People using the apps. From 14:00 on Oct 21 to the evening of Oct 22, connections failed or stalled for many people even though the app showed providers. The platform was down from 11:12 on Oct 23 to 03:20 on Oct 24, and again from 05:29 to 11:29. From 19:30 on Oct 24 to 01:27 on Oct 25, apps could find providers but could not send traffic.
Hosted proxy customers. Hosted proxies were hit first and hardest. From Oct 21 their windows churned through short-lived identities, and most of those identities never connected.
Providers. Providers could not connect while the platform was down.
Data. We know of no lost data.
What went wrong
Chapter 1A scheduled filter emptied provider pools and set off a connection storm
A scheduled change went ahead by default
The filter's start date was a default in code, so it switched on without anyone deciding. Our own measurements had shown it would remove more than 90% of one pool and leave the other with a single provider, and the deadline to decide passed without a decision.
Churn that kept itself going
Our hosted proxy service keeps windows of candidate providers, each tried with a newly created identity. With the pools empty most candidates failed, so windows created more. Every identity costs database work; the database slowed, connections took a median of 14 seconds, and windows discarded identities before they could connect, creating still more. The loop kept going after the filter was postponed.
Storage that only grows
Every short-lived identity left permanent records: a signed key registration stored twice, evidence objects, and contracts that are kept for months. The database grew by up to 20 GB an hour, and the disk-runway warning we planned after the Oct 12 near-miss had not been built.
Chapter 2The database disk filled, then payment reservations jammed
A logging fault on a nearly full disk
Once free space fell below the log service's reserve, the service started a new log file about three times a second and never removed old ones, writing about 200 GB in a few hours. The disk filled and the database stopped.
A storage expansion that couldn't run under load
The disk controller's online expansion made no progress while the database was serving, so finishing it took a second full outage.
Payment reservations released slower than they were made
Our hosted proxy service's network made hundreds of tiny contracts a second, each holding a reservation for up to 24 hours. A background job released them, but it waited about 96 seconds for each turn behind a backlog the same churn had built. The network's grants filled up, its new contracts failed, and two cache nodes saturated.
A backup that blocked cleanup
After recovery, a full backup held one view of the database for almost 7 hours, so the database could not clean up old row versions, and the busiest small tables grew to millions of dead rows.
What we changed
Each fix links to the public commits that made it. Changes to our servers' configuration are described without links.
Chapter 1A scheduled filter emptied provider pools and set off a connection storm
- DoneHosted proxy windows pace their search for providers after a pass that finds none.
- urnetwork/connect · 8033a89 ↗Merge discovery window expansion pacing
- DoneChurn brakes: hosted proxies keep their backoff across a successful search, reuse identities no provider has seen, create at most two identities at a time and race one candidate instead of two.
- urnetwork/connect · 24561f9 ↗Merge hosted window churn fixes (429 backoff, discovery identity reuse)
- urnetwork/server · ef3f5e6 ↗Merge client-key evidence churn brakes (content-key only, history expiry, mint cap 2, race 1x)
- DoneEach identity's key registration is stored once instead of twice, and the extra copies expire.
- urnetwork/server · e9f2573 ↗Merge client-key registration read from evidence (step 1 of 3)
- urnetwork/server · b878407 ↗Merge client-key registration stop-writing (step 2 of 3)
- DoneThe filter's start was postponed, first in configuration and then in code to no earlier than Nov 4.
- urnetwork/server · 47776d9 ↗Postpone modern provider gate until November 4 UTC
- DoneA new alert for runaway identity churn.
- urnetwork/server · 822f479 ↗Add the hosted identity churn probe (SIGNALS.md §14.8)
Chapter 2The database disk filled, then payment reservations jammed
- DoneThe database server's storage was expanded.
- DoneLoad balancers can be brought up after their network interfaces are renamed.
- urnetwork/warp · 1a190de ↗Let --initialize-unset bootstrap load-balancer blocks without selectors
- DonePayment reservations spread across all of a network's grants, a busy balance releases full batches in one pass, and the release job gets its own share of task time.
- urnetwork/server · aec74d3 ↗Spread a multi-grant payer's Redis admissions across its large free grants
- urnetwork/server · 066ba17 ↗Drain a hot debit balance's full batches within one flush page
- urnetwork/server · f68b7b9 ↗Give the debit flush and the payment sender claim-share lanes
- DoneThe busiest small tables are vacuumed every five minutes, and their bloated indexes are rebuilt.
- urnetwork/server · 1f2e5bd ↗Vacuum the churn tables every five minutes and rebuild their bloated indexes
- DoneAn alert on the storage runway and retention of every partitioned table.
- urnetwork/server · 22a52d8 ↗Watch every partition registry family's runway and retention
Still open
- OpenThe filter must refuse to turn itself on while it would empty a selection pool; for now it is only postponed.
- OpenAn alert well before the database disk reaches the log service's reserve, and on how fast the disk is filling.
- OpenRegular database backups that can't hold up cleanup: at a quiet hour with the snapshot age watched, or from a replica.
- OpenMore task-scheduling throughput, so release work can't fall behind a backlog again.