IL-0324 Wikimedia Foundation · 2025-03-12databasekubernetesobservability
Three MediaWiki deployments lost Memcached after a PHP 8.1 rollout and overloaded the External Storage database cluster
Scap, Wikimedia's MediaWiki deployment tool, builds and pushes MediaWiki images, updates helmfile-defaults with the latest image tag for each deployment and release, and runs helmfile on every MediaWiki deployment. PHP-FPM reads two environment variables from a ConfigMap called mediawiki-main-php-envvars, mounted as /etc/php/<X.Y>/fpm/env/envvars.inc: MCROUTER_SERVER, the Memcached and mcrouter address, which defaults to 127.0.0.1:11213, and STATSD_EXPORTER_PROMETHEUS_SERVICE_HOST, which is unset by default. The include directory is hardcoded per PHP version, /etc/php/7.4/fpm/env/*.inc in the publish-74 image and /etc/php/8.1/fpm/env/*.inc in the publish-81 image. The PHP 8.1 rollout was two patches: 1126607 in puppet switched mw-api-int, mw-parsoid and mw-jobrunner from the publish-74 to the publish-81 image flavour, and 1126650 in deployment-charts moved the envvars.inc mount to the 8.1 path.
Scap was run with -Dbuild_mw_container_image:False, a flag SREs commonly use because most changes need no image rebuild, and publish-81 was already built and cached. Nobody realised that the flag also makes Scap skip the helmfile-defaults update, so the three deployments kept the publish-74 image while the ConfigMap was mounted at the 8.1 path. The 7.4 include directory held only default values, so the pods ran without access to the Memcached cluster and without a valid prometheus StatsD address; the StatsD gap had no effect because MediaWiki also pushed stats to Graphite. Without Memcached, MediaWiki queried the databases for objects that are normally cached, and the load on the External Storage cluster and the core databases rose.
On 12 March responders lowered the load from cirrus-streaming-updater and changeprop-jobqueue and, at 13:44, reverted both patches and re-ran Scap. On 13 March the rollout was attempted again with patches 1127476 and 1127478, and the three deployments again ran without access to Memcached; after rolling restarts of changeprop-jobqueue and mw-api-int and disabling the categorymembership job, the patches were reverted at 11:46, mw-api-int was redeployed and given more replicas, and mw-jobrunner and mw-parsoid were redeployed by 14:06. The write-up names Scap, the php-fpm envvars.inc include file and the logstash-prometheus pipeline as the factors that caused the incident and delayed finding its root cause, and says coordination among the 9 responders was difficult.
- Severity
- –
- Scope
- mw-api-int, mw-parsoid and mw-jobrunner had high latency and database errors on 12 and 13 March; global traffic had about a 0.03-0.04% error rate and edits were affected very briefly
- Users affected
- –
- Region
- –
- Time to detect
- 14m
- Time to resolve
- 3h
Symptoms
On 2025-03-12 from 10:58 UTC, alerts for high backend response times came in, and the MediaWiki deployments mw-api-int, mw-parsoid and mw-jobrunner, which serve MediaWiki for non-user-facing purposes, showed high latency and database errors. The External Storage cluster, whose hosts store the compressed text of wiki page revisions, had a sharp rise in load and connections. The same happened again on 2025-03-13 from 11:15 UTC.
Most MediaWiki errors in channel: error at the time were database related. External Storage queries per second and connection counts jumped, and circuit breakers kept the databases from collapsing; the core databases followed a similar pattern. WANObjectCache and the SQLBlobStore keygroup showed a high rate of miss.compute keys, while Memcached and mcrouter traffic dropped sharply. User-facing traffic was barely affected: global traffic had an error rate of about 0.03-0.04%, edits were affected for a very short time, and p50 and p75 latency of mw-web and the External API did not change.
The signal that pointed at the cause was hard to see. The deployments produced about 2mil/min memcached error events in channel: memcached, more than logstash could process, so the channel:memcached logstash dashboard probably could not render until logstash worked through its backlog after the incident. Memcached errors collected by Prometheus spiked at 50k/min, but delays in the mw→logstash→prometheus-es-exporter→prometheus pipeline kept the MediaWikiMemcachedHighErrorRate alert from staying up: it went off and recovered quickly, and the matching Grafana graph showed no data during the incidents. 78 alerts were firing during the incident, and the team thought it likely that even a firing MediaWikiMemcachedHighErrorRate would have been taken for a side effect.
Responders first worked through the usual suspects for MediaWiki latency and database load: high traffic from a specific network or client, scheduled jobs gone wrong, and greedy changeprop-jobqueue jobs. On 12 March they reduced SUP parallelism in cirrus-streaming-updater and the concurrency of the categoryMembershipChange job, and assumed the Scap rollout was not the problem. By 13 March they were certain the Scap rollout was the cause, but it took until after the incident to understand how.
Symptom Database overloadHigh latencyHigh error rateLog floodMonitoring outage
Root cause
Root cause Faulty deployment
- Trigger
- At 10:44 UTC on 12 March a Scap deployment of the PHP 8.1 rollout for mw-api-int, mw-parsoid and mw-jobrunner was run with the flag -Dbuild_mw_container_image:False; the rollout was deployed with Scap again at 11:15 UTC on 13 March.
Detection
Alerts for high backend response times started at 10:58 UTC on 12 March, 14 minutes after the Scap deployment; 2 on-call engineers were paged. The MediaWikiMemcachedHighErrorRate alert that pointed at the cause fired only briefly because its log pipeline was overloaded.
Remediation
- Deployed a patch to cirrus-streaming-updater that reduced SUP parallelism
- Reduced the concurrency of the categoryMembershipChange job in changeprop-jobqueue
- On 12 March, reverted 1126607 and 1126650 and re-ran Scap
- On 13 March, rolling-restarted changeprop-jobqueue
- Rolling-restarted mw-api-int
- Disabled the categorymembership job in changeprop-jobqueue
- Reverted 1127476 and 1127478
- Redeployed mw-api-int to pick up the revert
- Increased the number of mw-api-int replicas
- Redeployed mw-jobrunner to pick up the revert
- Redeployed mw-parsoid to pick up the revert
Action items
- Reduce the number of messages sent through channel:Memcached during failures
- Add helm rollback functionality to Scap
- Decide whether Scap should update helmfile-defaults when -Dbuild_mw_container_image:False is set
- Consider removing envvars.inc from MediaWiki images
- Consider "Why" dashboards that give an overview of a group of related components together with relevant links to follow
- Coordinate incident responders better
- Be able to work with MediaWiki developers during incidents
Lessons
The write-up puts the weight not on the technical root cause but on why the team could not see, even after the 12 March incident, that three MediaWiki deployments were running without Memcached. Alert dashboards showed what was happening, such as many busy PHP workers, but not why; dashboards linking components that affect each other, such as SQLBlobStore keys and External Storage, would have helped. The alert that pointed in the right direction did not stay firing and would probably have been lost among 78 others. The incident coordinator could not tell who was working on what or who had joined or left, the channels were noisy, the external impact was judged more critical than it was, and trace.wikimedia.org was not used although it might have helped.
Sources
Related cases
IL-0356Every domain hosted on Railway returned HTTP 404 to new connections for five minutes because route lookups failedRailway · 2026same root cause · shared CI system, Database
IL-0136A deployment that cleared an internal service's caches at peak traffic sent 20 times the normal load to the Azure OpenAI metadata backend in Sweden Central, and health checks that depended on its database restarted instances in a loop for almost six hoursMicrosoft Azure · 2026same root cause · shared Database, In-memory cache
IL-0552CircleCI's archival database ran out of storage, slowing workflow and job starts and the display of workflows in the UICircleCI · 2025same root cause · shared CI system, Database
IL-0437Old pods from an unfinished Argo Rollouts canary could not start with a Helm-updated ConfigMap and died in a cascadeGunosy · 2023same root cause · shared Helm, ConfigMaps and Secrets
IL-0570Dropping an unused MySQL table stalled queries on its busy parent table and crowdworks.jp was hard to reach for a minuteCrowdWorks · 2026same root cause · shared Database
IL-0234An onboarding operator deployed without its scope file installed dev applications on other IONOS Kubernetes clustersIONOS Cloud · 2026same root cause · shared CI system
IL-0510MIT Learn's RDS PostgreSQL ran out of storage after a release and learn.mit.edu returned 503s and 504s for hoursMIT Open Learning · 2026same root cause · shared Database
IL-0380A compaction change in InfluxDB Cloud us-east-1 dropped data from compacted files, and queries missed data for weeksInfluxData · 2026same root cause · shared Database
IL-0369Railway's backend API went down when a column migration waited on locks and replicas could not get database connectionsRailway · 2025same root cause · shared Database
IL-0547Buildkite annotation queries timed out and drove database CPU up across shards after a migration removed an indexBuildkite · 2025same root cause · shared Database
IL-0315Railway's backend API went down when an index build locked a billion-row Postgres table and used up every connectionRailway · 2025same root cause · shared Database
IL-0327After a blue/green upgrade of CircleCI's workflows database every query hit disk, and workflow starts lagged or failedCircleCI · 2025same root cause · shared Database
IL-0307Supabase's Postgres connection pooler Supavisor 2.0 dropped connections for about 80% of us-east-1 tenantsSupabase · 2025same root cause · shared Database
IL-0554A database migration used up Sentry US's Postgres connections, and the site and event ingestion were down for 22 minutesSentry · 2024same root cause · shared Database
IL-0571STORES reservation returned bursts of 504 errors when a smart-list query held a database process for over 20 minutesSTORES · 2024same root cause · shared Database
IL-0293Cloud Bigtable in us-central1 had higher latency and connection errors for about 32 hours from low-rate packet lossGoogle Cloud · 2023same root cause · shared Database
IL-0523TANF-app production deployments were blocked for two months when a search-index migration ran out of database disk spaceU.S. Department of Health and Human Services · 2023same root cause · shared Database
IL-0567Aurora MySQL behind Cookie Run: OvenBreak hit 100% CPU after a sales-ranking feature launch, and players could not playDevsisters · 2023same root cause · shared Database
IL-0483Replicate's website and API returned slow responses and HTTP 500s for two hours as PostgreSQL connections ran outReplicate · 2023same root cause · shared Database
IL-0371Half of new connections to Render Postgres databases with point-in-time recovery failed while their backup jobs ranRender · 2023same root cause · shared Database
IL-0524An index migration locked Open Collective's whole Transactions table and degraded the service for nearly an hourOpen Collective · 2023same root cause · shared Database
IL-0328A field type change in the database of CircleCI's job distribution service left all jobs stuck in "not running"CircleCI · 2021same root cause · shared Database
IL-0566Hot ranges after a Cookie Run: Kingdom guild update drove CockroachDB nodes to 100% CPU, forcing emergency maintenanceDevsisters · 2021same root cause · shared Database
IL-0467Boskos crash-looped after a node drain started it from a buggy StatefulSet image, failing Kubernetes PR jobs for 9 hoursKubernetes project · 2019same root cause · shared CI system
IL-0466Prow scheduled test job pods onto its service cluster, leaving hundreds Pending and starving Prow's own servicesKubernetes project · 2019same root cause · shared CI system
IL-0278A weekly update put a Redis Cloud management process into an infinite loop, and most nodes of a GCP cluster failedRedis · 2016same root cause · shared In-memory cache
IL-0391GoCardless API requests timed out for about 15 seconds while a Postgres migration waited for an AccessExclusive lockGoCardless · 2015same 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-0324
- The cases most like it: https://incidentlib-api.benchlet.com/cases/IL-0324/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.