Page MenuHomePhabricator

Investigate high saturation in codfw
Open, HighPublic

Description

Conclusion

Summary posted in T438896#12371432. Much thanks to @CDanis for the investigation and summary.

So before the big ol' power outage made everyone's day a lot more eventful, we had been trying to do the switchover to codfw again.

Our first attempt to move eqiad's read-only traffic to codfw failed -- appservers saturated and latencies skyrocketed. So we increased the number of appserver pods, hoping that was the bottleneck, and tried again, for about 10 minutes from 15:04 UTC to 15:15 UTC. Same thing happened even after more appserver capacity, suggesting the bottleneck was in fact elsewhere, and so we went looking for where and why (until the power outage struck).

I downloaded all the mw-web @ codfw traces from this particular overload window, 2026-09-23 15:04-15:15 UTC, and I vibe-scripted-analyzed them together with Claude. We only have traces for 0.1% of the traffic from interval, but for those 769 requests, we have extremely detailed timing information for different stages of Mediawiki's processing. I think they leave us with some very good pointers on what went wrong.

Big takeaways

  • The single biggest contributor to the overload was the cumulative queuing times in Envoy + Apache: 1138.9 seconds (!!) across 769 requests. (This time also includes PHP worker startup, but that should be small.)
  • In the initial minutes of the outage, we spent a lot of Mediawiki time on s7 database calls, which caused many queued requests to pile up -- mostly on load.php. Once the various caches had been somewhat warmed, this subsided. But after that, Mediawiki re-focused its death ray on eventgate and/or kafka, and in the later minutes we spent much time blocked waiting for eventgate-main to flush in post-send.
    • We saw a similar but much worse version of this during the eqiad capacity shortfall while codfw was lights-out, where it appears that almost all Mediawikis were blocked on eventgate/kafka for perhaps as long as 10 minutes (!!)
  • After queuing time, the largest single cost in MW was DB time: 87.1s of cumulative database wall time, predominantly against s7 replicas.
    • 35% of this DB time (30.1s/87.1s) comes from the BEGIN/USE/SET/COMMIT dance that Mediawiki RDBMS does upon every new database connection. Again, mostly from s7 replicas, which must have been under very high contention to be responding so slowly to almost-no-op queries.
    • Another 30% (26.0s) of the database time comes from the single traced request that executes the Campaign::getCampaignMixins queries. It shows as 44 SELECTs made in quick succession to one s7 replica, taking on average 600ms each.
  • Close behind: 83.2s cumulative time blocked waiting on eventgate-main (this very likely under-counts the true blocked time)
  • 51.1s cumulative time on getParserOutput and children (n.b. some database time is included in this)
  • 45.9s cumulative WANObjectCache::getWithSetCallback wall time -- and as above some database time is included in this, including and especially 100% of the getCampaignMixins time.
    • There is no stampede protection on this CentralNotice code path that calls getCampaignMixins, and, the cache key is different for each (wiki, user interface language) pair. The cache keys in question are looked up by every load.php fetch that involves any sort of JS, even if ext.centralNotice.choiceData isn't requested, as in this trace.
    • The regeneration costs for this WANObjectCache key (see F105294207) also look to correlate with the September 1st suspected Mediawiki performance regression T438962.
    • Given that the regeneration makes N serial database roundtrips, where N is the number of relevant campaigns, and that, as best I can tell, the number of active campaigns greatly increased starting around 2026-08-31, this confluence of code and data is likely a huge source of the cold-start problem in codfw, and a significant contributor to overall Mediawiki appserver load (the bare minimum load is hourly recomputations of ~thousands of these keys).
    • Cold starting codfw is extra-hard because the traffic mix for wikis and user languages likely differs substantially between core datacenters.

What needs to change ASAP

MediaWiki-extensions-CentralNotice

  • ChoiceDataProvider::fetchChoices must not call Campaign::getCampaignMixins for each campaign by name in a for loop; it should instead fetch mixins by campaign ID, all at once, with not_id IN (...), as it does for regions and countries just before the foreach.
  • ChoiceDataProvider::getChoices must modify its WANObjectCache::getWithSetCallback() invocation to include stampede protection. I think all that is needed is adding something like 'lockTSE' => 30, to the options dictionary. (The long-extant comment is dangerously wrong under cold-start conditions.)

Event-Platform

  • We must either always set ?hasty=true when calling EventGate from Mediawiki, even when in post-send, or, we must implement a much shorter (and perhaps randomized) timeout for EventGate requests.

What should probably change in the near term

DBA

  • We have only 7 replicas of s7 in codfw, compared to 13 in eqiad. This very likely made the chained selects and lack of stampede protection much more harmful there. Is it worth upsizing?

ServiceOps

  • Up-size the memcached cluster in codfw.

To consider in the longer term

  • Some mechanism for proportional (weighted / partial) pooling of a DNS/Discovery service, even if the mechanism is "ATS-only" and/or gross. It's very likely we'll need this for cold starts for other disaster recovery scenarios, and it makes cold starts much safer.
Previously reflected (and now outdated) context

Context
Shortly following T435443: Day 1 - Source datacenter depooling (Tue. Sept. 22nd - 2026), we were paged for PHP-FPM saturation in codfw. At current, eqiad is depooled for read-only traffic with the exception of mediawiki services and rest-gateway, which were repooled shortly following the saturation page.

Update: As of 18:00 UTC Thursday, codfw has been repooled for both edge & traffic following the power outage in T439010: codfw power outage

Investigation summary
Possibly this is related to thumb.wikimedia.org and the work in T427465: Move thumbnail caching from upload cluster to text, moving thumb.wm.o to cache-text. Specifically the theory here being that the working set of thumb.wm.o + en.wp.o & others is too large following the move. The resulting increase in request rate to mw-web, we might have encountered an (unrealized) situation where we no longer have the capacity we should with just a single DC servicing requests.

Thanks to @RLazarus and @Scott_French who've pointed out that TTL on frontend cache is 7 days [0] which lines up rather accurately with when the thumbs.w.o change is made and when traffic starts to spike a week later on the 14th, so granted that the image urls are baked into the cache response, we suspect that cache-text would have still been serving responses with pre-thumb.w.o image URLs for those 7 days before the TTL expired.

Next steps
A few options, we could consider:

  1. Reverting the change, either wait 7 days or purge to speed things up - our pre thumb.w.o estimates become valid again
  2. Increasing app layer capacity to support post-thumb-wm-o cache behavior
  3. Doing something smart to the caches to somehow get back the hit rates we used to have?

Impact on switchover timeline
If we plan to go with the revert+wait route, we will need to hold off on the mediawiki switchover, at least for tomorrow, while we investigate this further. Alternatively, if we choose to increase app layer capacity to support post-thumb-wm-o cache behaviour, it is possible that we might be able to proceed, although we should take further consequences & impact into consideration before doing so.

Edit: The switchover has been definitively postponed while codfw power stabilizes. See T439010: codfw power outage

Testing & recovery timeline
On Wednesday Sept. 23rd, around 15:10 UTC, we attempted to repool mw-web-ro in codfw and observe how it might hold up. This comes after repooling eqiad for edge cache. During the mw-web-ro repool in codfw, we saw saturation hit 100% then fall quickly thereafter. The test pool was then depooled while we investigated further.

A rule was enabled banning scrapers shortly following then reverted: https://phabricator.wikimedia.org/T438896#12356408
Some patterns observed during the saturation spike during the test window: https://phabricator.wikimedia.org/T438896#12356442

Shortly after, at around 17:21 UTC, codfw lost power in multiple rows, which prompted a emergency switchover back to eqiad. Incident follow up in T439010: codfw power outage The switchover has been definitively postponed while codfw power stabilizes.

[0] - https://gerrit.wikimedia.org/g/operations/puppet/+/production/modules/varnish/templates/wikimedia-frontend.vcl.erb#916

Related Objects

Event Timeline

There are a very large number of changes, so older changes are hidden. Show Older Changes
Restricted Application added a subscriber: Aklapper. ยท View Herald TranscriptTue, Sep 22, 6:12 PM
jasmine_ renamed this task from Investigate high saturation in codfw during switchover to Investigate high saturation in codfw during switchover resulting from increased thumb.wm.o cache pressure.Wed, Sep 23, 3:17 AM
jasmine_ updated the task description. (Show Details)
jasmine_ added a subscriber: Scott_French.

Traffic team has moved 14 hosts from upload to text which should have increased its capacity and thus reducing the load (T436363). I don't know if we should move more or problem lies somewhere else.

MLechvien-WMF moved this task from Inbox to In Progress on the ServiceOps board.
MLechvien-WMF subscribed.

Traffic could you help us looking into this ?

Change #1344212 had a related patch set uploaded (by Blake; author: Blake):

[operations/deployment-charts@master] mw-web: upsize for single-DC serving.

https://gerrit.wikimedia.org/r/1344212

As an action to at least partially unblock the switchover, we could finally move to block all requests from clients that:

  • have a very low browser score
  • do not honor our UA policy

it is a very radical action, but it would shave off about 15-20% of all uncached requests to mw-web: see https://w.wiki/V5tD

As an action to at least partially unblock the switchover, we could finally move to block all requests from clients that:

  • have a very low browser score
  • do not honor our UA policy

it is a very radical action, but it would shave off about 15-20% of all uncached requests to mw-web: see https://w.wiki/V5tD

I would say this is overdue and we should enable that rule.

Change #1344212 merged by jenkins-bot:

[operations/deployment-charts@master] mw-web: upsize for single-DC serving.

https://gerrit.wikimedia.org/r/1344212

Mentioned in SAL (#wikimedia-operations) [2026-09-23T10:22:07Z] <blake@deploy1003> Started scap sync-world: Upsize mw-web T438896

Mentioned in SAL (#wikimedia-operations) [2026-09-23T10:25:40Z] <blake@deploy1003> Finished scap sync-world: Upsize mw-web T438896 (duration: 04m 20s)

Mentioned in SAL (#wikimedia-operations) [2026-09-23T10:38:17Z] <jayme> sudo confctl --quiet --object-type discovery select 'dnsdisc=mw-web-ro' set/ttl=10 - T438896

<effie> eqiad has double the memcached capacity codfw has atm, as we are waiting for the refresh. While not the sole reason for the swithover issues, it is a contributing factor
<effie> iirc (please check), codfw still has extstore enabled, using disk to cache objects-to-be-evicted, this could be increased easily, though it will require restarting the daemons

T352885: Enable extstore to a subset of memcached servers (experiment)

Discussing it on IRC some this morning, we decided that it was also worth trying a small deviation from the normal procedure, and leaving eqiad's CDN edge pooled. The idea is that the working set of hot content differs between eqiad and codfw, so, let's keep our eqiad cache capacity online, and not force a larger working set into codfw's edge.

This isn't that unfaithful to an "eqiad deleted" scenario -- in such a case we would probably consider redistributing eqiad's load across a few other sites anyway.

If this works out, it would still allows us to test mw-web capacity, do eqiad database maintenances, etc.

eqiad has been pooled for a little over half an hour, and the "true hitrate" (hit/(hit+miss)) is at 77% and climbing:

image.png (2,139ร—803 px, 259 KB)

While eqiad was depooled, codfw had this figure around 81-82%, so eqiad is looking pretty warm already.

it is a very radical action, but it would shave off about 15-20% of all uncached requests to mw-web: see https://w.wiki/V5tD

This rule was activated during testing today and it immediately led to noc feedback and CI pipelines in gitlab failing so it was disabled again.

Things that where found in mw traces:

jasmine_ renamed this task from Investigate high saturation in codfw during switchover resulting from increased thumb.wm.o cache pressure to Investigate high saturation in codfw possibily resulting from increased thumb.wm.o cache pressure.Wed, Sep 23, 8:07 PM
jasmine_ updated the task description. (Show Details)
jasmine_ updated the task description. (Show Details)

Things that where found in mw traces:

This has been an issue in previous outages (2025-05-21 when we discovered the cpu governor issue, notably) where these timeouts tying up workers lead to amplified pressure elsewhere in the infra. We should definitely drop this to something low

@VPuffetMichel Hi, would you mind asking someone in the Connection team to take a look at this? Thank you!

Change #1344739 had a related patch set uploaded (by Hnowlan; author: Hnowlan):

[operations/puppet@production] services_proxy: drop eventgate timeout to 11s

https://gerrit.wikimedia.org/r/1344739

Change #1344739 merged by Hnowlan:

[operations/puppet@production] services_proxy: drop eventgate timeout to 11s

https://gerrit.wikimedia.org/r/1344739

So before the big ol' power outage made everyone's day a lot more eventful, we had been trying to do the switchover to codfw again.

Our first attempt to move eqiad's read-only traffic to codfw failed -- appservers saturated and latencies skyrocketed. So we increased the number of appserver pods, hoping that was the bottleneck, and tried again, for about 10 minutes from 15:04 UTC to 15:15 UTC. Same thing happened even after more appserver capacity, suggesting the bottleneck was in fact elsewhere, and so we went looking for where and why (until the power outage struck).

I downloaded all the mw-web @ codfw traces from this particular overload window, 2026-09-23 15:04-15:15 UTC, and I vibe-scripted-analyzed them together with Claude. We only have traces for 0.1% of the traffic from interval, but for those 769 requests, we have extremely detailed timing information for different stages of Mediawiki's processing. I think they leave us with some very good pointers on what went wrong.

Big takeaways

  • The single biggest contributor to the overload was the cumulative queuing times in Envoy + Apache: 1138.9 seconds (!!) across 769 requests. (This time also includes PHP worker startup, but that should be small.)
  • In the initial minutes of the outage, we spent a lot of Mediawiki time on s7 database calls, which caused many queued requests to pile up -- mostly on load.php. Once the various caches had been somewhat warmed, this subsided. But after that, Mediawiki re-focused its death ray on eventgate and/or kafka, and in the later minutes we spent much time blocked waiting for eventgate-main to flush in post-send.
    • We saw a similar but much worse version of this during the eqiad capacity shortfall while codfw was lights-out, where it appears that almost all Mediawikis were blocked on eventgate/kafka for perhaps as long as 10 minutes (!!)
  • After queuing time, the largest single cost in MW was DB time: 87.1s of cumulative database wall time, predominantly against s7 replicas.
    • 35% of this DB time (30.1s/87.1s) comes from the BEGIN/USE/SET/COMMIT dance that Mediawiki RDBMS does upon every new database connection. Again, mostly from s7 replicas, which must have been under very high contention to be responding so slowly to almost-no-op queries.
    • Another 30% (26.0s) of the database time comes from the single traced request that executes the Campaign::getCampaignMixins queries. It shows as 44 SELECTs made in quick succession to one s7 replica, taking on average 600ms each.
  • Close behind: 83.2s cumulative time blocked waiting on eventgate-main (this very likely under-counts the true blocked time)
  • 51.1s cumulative time on getParserOutput and children (n.b. some database time is included in this)
  • 45.9s cumulative WANObjectCache::getWithSetCallback wall time -- and as above some database time is included in this, including and especially 100% of the getCampaignMixins time.
    • There is no stampede protection on this CentralNotice code path that calls getCampaignMixins, and, the cache key is different for each (wiki, user interface language) pair. The cache keys in question are looked up by every load.php fetch that involves any sort of JS, even if ext.centralNotice.choiceData isn't requested, as in this trace.
    • The regeneration costs for this WANObjectCache key (see F105294207) also look to correlate with the September 1st suspected Mediawiki performance regression T438962.
    • Given that the regeneration makes N serial database roundtrips, where N is the number of relevant campaigns, and that, as best I can tell, the number of active campaigns greatly increased starting around 2026-08-31, this confluence of code and data is likely a huge source of the cold-start problem in codfw, and a significant contributor to overall Mediawiki appserver load (the bare minimum load is hourly recomputations of ~thousands of these keys).
    • Cold starting codfw is extra-hard because the traffic mix for wikis and user languages likely differs substantially between core datacenters.

What needs to change ASAP

MediaWiki-extensions-CentralNotice

  • ChoiceDataProvider::fetchChoices must not call Campaign::getCampaignMixins for each campaign by name in a for loop; it should instead fetch mixins by campaign ID, all at once, with not_id IN (...), as it does for regions and countries just before the foreach.
  • ChoiceDataProvider::getChoices must modify its WANObjectCache::getWithSetCallback() invocation to include stampede protection. I think all that is needed is adding something like 'lockTSE' => 30, to the options dictionary. (The long-extant comment is dangerously wrong under cold-start conditions.)

Event-Platform

  • We must either always set ?hasty=true when calling EventGate from Mediawiki, even when in post-send, or, we must implement a much shorter (and perhaps randomized) timeout for EventGate requests.

What should probably change in the near term

DBA

  • We have only 7 replicas of s7 in codfw, compared to 13 in eqiad. This very likely made the chained selects and lack of stampede protection much more harmful there. Is it worth upsizing?

ServiceOps

  • Up-size the memcached cluster in codfw.

To consider in the longer term

  • Some mechanism for proportional (weighted / partial) pooling of a DNS/Discovery service, even if the mechanism is "ATS-only" and/or gross. It's very likely we'll need this for cold starts for other disaster recovery scenarios, and it makes cold starts much safer.
Ladsgroup renamed this task from Investigate high saturation in codfw possibily resulting from increased thumb.wm.o cache pressure to Investigate high saturation in codfw.Mon, Sep 28, 9:02 PM

Thank you for the investigation Chris! <3 I updated the title to reflect the fact that thumb migration is likely not the cause. Also the Campaigns is CN feature and I got confused. Sorry for the wrong ping Val.

Thank you for the investigation Chris! <3 I updated the title to reflect the fact that thumb migration is likely not the cause. Also the Campaigns is CN feature and I got confused. Sorry for the wrong ping Val.

+1 Thanks @CDanis !

& Thanks for updating the title @Ladsgroup, apologies for the false positive! Updating the description to reflect this similarly.

I put together a cool interactive data visualization for the overload event, along with some explainers. If you're interested in this incident, or the approach used to diagnose the causes, check it out: https://people.wikimedia.org/~cdanis/2026-switchover-overload/