MongoDB's WiredTiger storage engine caches most of the working set in memory — what actually happens when your data set outgrows the WiredTiger cache, and how do you tell that's the problem versus a missing index?
Quick Answer
Once the active working set no longer fits in the WiredTiger cache (by default about 50% of RAM minus 1GB), MongoDB has to evict pages and fetch data back from disk on subsequent access, which shows up as rising page fault rates and increasing read latency even on queries that are already using the right index. This is distinct from a missing-index problem because explain() will still show a clean IXSCAN with a good docsExamined ratio — the slowdown is coming from storage I/O, not the query plan.
Detailed Answer
Picture a chef working in a kitchen with a small countertop (memory) and a much larger pantry down the hall (disk). As long as all the ingredients for today's menu fit on the counter, everything is fast — grab and go. The moment the menu needs more ingredients than the counter can hold, the chef has to keep walking to the pantry and back mid-recipe, and every dish takes longer, even though the chef is following the exact same recipe (query plan) as before.
MongoDB's default storage engine, WiredTiger, keeps a cache of frequently accessed data and indexes in memory, sized by default to roughly 50% of total RAM minus 1GB (with a floor of 256MB), specifically because leaving the rest of RAM for the OS file system cache and other processes was found to perform better than giving WiredTiger everything. This design assumes the 'hot' working set — the data actually being read and written by current traffic — fits inside that cache; MongoDB was never designed to require your entire dataset to fit in RAM, only the actively used portion of it.
When the working set exceeds the cache, WiredTiger has to evict pages (remove cached data to make room for new reads) using an LRU-like (least recently used) policy, and any subsequent access to evicted data triggers a page fault requiring a read from disk. This happens even for a query that is using a perfectly appropriate index, because the index itself is also subject to caching — a large index that doesn't fit in cache means even the index lookup incurs disk reads, not just the document fetch. This is the key diagnostic distinction from a query-plan problem: explain() still reports IXSCAN with a tight docsExamined-to-nReturned ratio, because the query planner did its job correctly; the slowdown is purely a storage-layer cache-miss cost that explain() doesn't directly surface.
In production, engineers watch wiredTiger.cache statistics from db.serverStatus() — specifically "bytes currently in the cache" against the configured cache size, and "pages read into cache" as a rate — alongside OS-level disk read IOPS and page fault counts. A cluster whose cache hit ratio (tracked in db.serverStatus().wiredTiger.cache) is dropping while query plans remain unchanged is a strong signal of a memory-sizing problem rather than an indexing problem, and the fix is either scaling to a larger instance with more RAM, sharding to spread the working set across more nodes, or reducing the working set itself (e.g., archiving cold data out of the hot collection).
The gotcha that catches teams sizing their clusters: adding more indexes to 'fix' slow queries can make this problem worse, not better, because every index also competes for the same WiredTiger cache space as the actual document data. A collection with five large secondary indexes might have a working set that technically includes documents plus all five indexes, and if that combined footprint exceeds the cache, you can end up in a state where adding the index that was supposed to speed up one query pushes another previously-fast query's data out of cache, trading one latency problem for a different one that shows up on a completely unrelated endpoint.