IL-0312 Gadget · 2024-12-13database
Gadget's main Postgres did not start again after a restart to raise max_connections, and the platform went down
Gadget runs one main Postgres database for all application data, with PgBouncer pooling connections from the API servers' Node.js processes. Gadget had planned for a doubling of traffic over Black Friday / Cyber Monday but saw almost 5x the load, and max_connections was no longer high enough. PgBouncer fetches each user's password with an auth_query at connection time, the only PgBouncer authentication mode Gadget's database vendor supports, so new connections doubled during traffic spikes and caused small disruptions. Raising max_connections requires a full restart. The vendor offers an in-place restart or a failover to a new replica built with the new configuration; because the database holds many terabytes and a replica takes a long time to build, Gadget chose the in-place restart, expecting less downtime.
Postgres shut down at 11:25 AM and failed to come back at 11:26, because months of uptime had fragmented the kernel's memory and the huge_pages allocations could not be met. The cause was identified at 11:45, and a hardware reset of the database server cleared the memory so Postgres started at 11:50. The platform was fully unavailable for 35 minutes. Compute that the autoscalers had removed during the outage then had to be scaled back up almost 4x, and all metrics recovered at 12:10 PM. Shopify webhooks were delayed but not lost, because Shopify retried delivery and Gadget's nightly reconciliation re-ran model actions for missed deliveries.
- Severity
- –
- Scope
- All requests to Gadget applications: fully unavailable for 35 minutes after the restart, then elevated latency until 12:10 PM EST; Shopify webhook data delayed but not lost
- Users affected
- All Gadget applications
- Region
- –
- Time to detect
- 1m
- Time to resolve
- 45m
Symptoms
At 11:25 AM EST on 13 December 2024, Gadget restarted its main PostgreSQL server in place to raise max_connections. Postgres shut down but did not start again, and every request to Gadget applications failed, including frontend requests, GraphQL API requests, webhook processing and syncs.
The new Postgres process could not start because the kernel could not give it the memory it asked for. Postgres runs with huge_pages=on and allocates many gigabytes for its shared_buffer pools at startup. After months of continuous operation the server's memory had become fragmented, so there was enough free memory in total but not enough contiguous free memory for the huge page allocations. Neither Gadget nor its database vendor knew this could happen, and the cause was found at 11:45 AM.
After the database came back at 11:50 AM, requests were processed with elevated latency until 12:10 PM. During the outage Gadget's compute autoscalers had seen less load and scaled down, so almost 4x the compute had to be provisioned after recovery, which strained the provisioning systems and added about 20 minutes in which some applications' requests were much slower.
The restart was itself a response to earlier, very small disruptions. Behind PgBouncer, each new gadget_api connection briefly opens two Postgres connections, one as superuser for the auth_query that fetches the user's password and one for the pooled connection, so traffic spikes quickly reached max_connections and returned errors to PgBouncer. PgBouncer retries such errors very slowly, so frontend API processes timed out on queries, and an API server whose SELECT 1 health check cannot run fast enough is marked unhealthy and removed from the load balancer.
Symptom Service outageHigh latency
Root cause
Root cause Capacity exhaustion
- Trigger
- An emergency in-place restart of the main Postgres server at 11:25 AM EST, decided at 11:00 AM, to raise max_connections, which Postgres cannot change without a full restart.
Detection
The team carrying out the planned restart saw at 11:26 AM EST that Postgres had shut down and did not start again.
Remediation
- Restarted the database server with a hardware reset, which cleared the fragmented memory so Postgres could start
- Provisioned almost 4x the compute after the autoscalers had scaled it down during the outage
Action items
- Use only the replica-failover approach for disruptive database operations such as restarts
- Run a more aggressive capacity planning process before Black Friday / Cyber Monday, preparing for a 6x increase in load and proving it with load tests well in advance
- Announce planned downtime ahead of time on the status page and community Discord, and make the operating procedures tell planned maintenance apart from service disruptions
- Finish the database sharding project so apps can use different databases and configuration changes can go through canary and blue/green setups
- Finish building a zero-downtime database failover system
- Set much higher scaling floors on the compute autoscalers
Lessons
Gadget names three deeper factors. Choosing the in-place restart put the database into a state the team had not seen before and was not confident in. Capacity planning for Black Friday / Cyber Monday assumed a doubling of traffic instead of the almost 5x that came, so settings such as max_connections stopped fitting and had to be changed in an emergency instead of in a maintenance window practised in staging. And there was no way to test database configuration changes in a production-like environment: integration tests run against a synthetic Postgres database that never goes through the months of production load behind the memory fragmentation or the memory usage issues.
Sources
Related cases
IL-0302Peak load on GitHub's mysql1 MySQL cluster maxed out its database proxy's connections and stopped writes in March 2022GitHub · 2022same root cause · shared Database, Database proxy and connection pooler
IL-0064CPU-heavy pods packed onto one Google Cloud node drove it to kernel panics, and the crash moved from node to node until the site was downMoonlight · 2019same root cause · shared Linux kernel, Database
IL-0450Oracle RAC commits behind order APIs kept waiting over 0.1 s on log file sync while average host CPU had headroomToss Securities · 2026same root cause · shared Database
IL-0553CircleCI's orchestration database filled its transaction log volume, and no workflows or jobs started for 50 minutesCircleCI · 2026same root cause · shared Database
IL-0379InfluxDB Cloud queries in Azure West Europe slowed and timed out as storage pods fell behind and kept restartingInfluxData · 2026same root cause · shared Database
IL-0309Preset's RDS PostgreSQL 14.7 to 17.9 upgrade in US East 1 ran out of storage and took Superset down for six hoursPreset · 2026same root cause · shared Database
IL-0435Error rates rose as Aurora MySQL killed connections and declined queries to avoid OOM while load metrics stayed flatCyberAgent · 2026same root cause · shared Database
IL-0497Buttondown's database ran out of connections twice in one day and requests failed while health checks mostly passedButtondown · 2026same root cause · shared Database
IL-0288ZooKeeperWait rose to thousands of seconds on three redundant ClickHouse instances, degrading a CDN77 data pipelineCDN77 · 2026same root cause · shared Database
IL-0350Hosts in an IONOS TXL cluster lost connectivity when an InfiniBand fabric resweep failed on a full multicast tableIONOS Cloud · 2026same root cause · shared Database
IL-0305Replicas of one Intercom Vitess shard hit 100% CPU and fell up to 2.2 hours behind, serving stale inbox dataIntercom · 2026same root cause · shared Database
IL-0390PostHog US Cloud event ingestion slowed to delays of up to 2 days as writes to the Persons Postgres table stalledPostHog · 2025same root cause · shared Database
IL-0100PostHog's feature flags pods were packed onto Kubernetes nodes that were too small for their CPU requests, and CPU saturation above 90% caused three outages in ten days through Redis overload, connection pool exhaustion and slow queriesPostHog · 2025same root cause · shared Database
IL-0287One overloaded shard of MongoDB's internal monitoring database delayed Atlas metrics, alerts and cluster operationsMongoDB · 2025same root cause · shared Database
IL-0557Database transaction locks piled up on Storj's US1 satellite, and uploads and downloads failed with timeouts for 2 hoursStorj · 2025same root cause · shared Database
IL-0551Bitbucket Cloud's overloaded primary database failed over, and the website, API and Pipelines were slow for almost a dayAtlassian · 2025same root cause · shared Database
IL-0298Wikimedia edits failed for 38 minutes when sessionstore Cassandra nodes in both data centres crashed on full disksWikimedia Foundation · 2025same root cause · shared Database
IL-0440Pods on Cybozu's Neco Kubernetes platform got frequent no such host and i/o timeout DNS errors from dropped packetsCybozu · 2024same root cause · shared Linux kernel
IL-0544Wasabi's database cache stopped responding when its queueing service filled up, and S3 failed in all regionsWasabi · 2024same root cause · shared Database
IL-0447An every RDS for MySQL instance slowed badly when its gp2 EBS BurstBalance ran out and IOPS fell to the baselineevery · 2024same root cause · shared Database
IL-0375MongoDB Atlas cluster changes and alerts lagged for hours after a control plane metadata migration congested a databaseMongoDB · 2024same root cause · shared Database
IL-0110A Grafana Agent pod's high memory use and crash-looping Hangfire pods drove a Kubernetes node into system OOM and NotReady, twice in one dayDFDS · 2024same root cause · shared Database
IL-0550Bitbucket Cloud's database slowed until connection pools saturated and requests timed out, leaving Pipelines pendingAtlassian · 2024same root cause · shared Database
IL-0270Discord's ScyllaDB authentication cluster overloaded during an OS upgrade, and logins failed for about 50 minutesDiscord · 2023same root cause · shared Database
IL-0202A burst of queries and database writes exhausted connection pools and memory on Honeycomb's ingest hosts, and Kubernetes crash-loop back-off kept failed hosts offline so ingest capacity stayed too low to recoverHoneycomb · 2023 · sev1same root cause · shared Database
IL-0386After a move to Google Cloud, Postgres connection timeouts and memcached i/o timeouts spiked on GKE nodesincident.io · 2023same root cause · shared Database
IL-0326A retention clean-up job filled the disk of MailPace's managed Postgres, which stayed offline for just over two hoursMailPace · 2023same root cause · shared Database
IL-0546Buildkite jobs could not go to newly connected agents once agent IDs passed a 32-bit integer foreign key's limitBuildkite · 2023same root cause · shared Database
IL-0500UToronto JupyterHub logins and server starts returned 500 errors when the hub's sqlite database disk filled up2i2c · 2022same root cause · shared Database
IL-0301Basecamp 3's primary database went into total gridlock at the top of the hour and Basecamp 3 went down twice37signals · 2022same root cause · shared Database
IL-0206Consul agents in GitLab.com's GKE pods were repeatedly OOM-killed, so the Rails pods lost database replica discovery and sent every query to the primaryGitLab · 2022same root cause · shared Database
IL-0212Many Slack users could not connect because a Vitess keyspace for channel membership was overloaded by cache-miss queriesSlack · 2022same root cause · shared Database
IL-0456Inflearn went down six times during a January 2022 free-course event as slow queries piled up on Aurora PostgreSQLInflab · 2022same root cause · shared Database
IL-0321GCP snapshots on all Patroni nodes stalled GitLab.com's primary PostgreSQL disk, Patroni failed over and 5xx spikedGitLab · 2021same root cause · shared Database
IL-0176Prometheus in a 235-node Kubernetes cluster crash-looped because over two million series exhausted host memory and the kernel OOM killer chose it first, as Burstable QoS gave it oom_score_adj 999LINE · 2020same root cause · shared Linux kernel
IL-0578Static content behind an HAProxy load balancer in an OpenStack Octavia amphora was delayed by tens of secondsGMO Pepabo · 2020same root cause · shared Linux kernel
IL-0496Ably's Cassandra hit 100% CPU in every region and queries timed out globally, and the load returned on two more eveningsAbly · 2020same root cause · shared Database
IL-0443A Repro Aurora MySQL reader restarted unexpectedly and writer insert latency rose about sevenfold for a day or moreRepro · 2019same root cause · shared Database
IL-0037Image registry rate-limited a whole region after a crash-looping DaemonSet with imagePullPolicy Always pulled through three NAT instancesDatadog · 2019same root cause · shared Database
IL-0297Mezmo users could not sign in and log indexing lagged after the auth cache's Redis servers were OOM killedMezmo · 2019same root cause · shared Database
IL-0211A Mandrill PostgreSQL shard shut down at transaction ID wraparound and email sending was impaired for about 40 hoursMailchimp · 2019same root cause · shared Database
IL-0472Node-to-pod traffic and DNS failed on Tinder's 605-node Flannel cluster when every node's ARP cache passed gc_thresh3Tinder · 2019same root cause · shared Linux kernel
IL-0300Basecamp 3 went read-only for almost five hours when its events table reached the ceiling of 2,147,483,64737signals · 2018same root cause · shared Database
IL-0334Instapaper's RDS MySQL bookmarks table hit the 2TB ext3 file size limit, inserts failed and the service went downInstapaper · 2017same root cause · shared Database
IL-0333A downsized RDS PostgreSQL database maxed out its CPU at US peak and failing ELB health checks took buildkite.com downBuildkite · 2016same root cause · shared Database
IL-0282DynamoDB in US-East reached about 55% request errors as storage servers overloaded its metadata serviceAmazon Web Services · 2015same root cause · shared Database
IL-0383PagerDuty's shared Cassandra cluster degraded during a repair, delaying notifications and losing incoming eventsPagerDuty · 2014 · sev1same root cause · shared Database
IL-0320Postmark's primary MySQL disk filled but MySQL kept running, so MySQL-MMM never failed over and sending was delayedPostmark · 2011same root cause · shared Database
Check your cluster for this
For AI tools and scripts
- This case as JSON: https://incidentlib-api.benchlet.com/cases/IL-0312
- The cases most like it: https://incidentlib-api.benchlet.com/cases/IL-0312/similar
- MCP server (Streamable HTTP, no key):
https://incidentlib-mcp.benchlet.com/mcp
The API needs no key. The MCP server offers search_incidents, get_incident, list_incidents, get_topic and vocab; both are explained on API and MCP.