MongoDB tiene un límite de 100 MB de RAM por etapa de agregación. Lo descubrí cuando un reporte de BI que funcionaba bien de repente empezó a fallar con un error de memoria. La solución técnica era allowDiskUse: true, pero antes de activarla quise entender qué estaba pasando realmente.
Una cosa es saber que existe esa opción, y otra es entender por qué usarla puede ser peor que el problema original. Acá va lo que aprendí sobre allowDiskUse, sus riesgos y cómo evitarlo por completo.
El Problema
MongoDB procesa las agregaciones en memoria RAM. Por defecto, cada etapa del pipeline ($match, $group, $lookup, etc.) tiene un límite de 100 MB.
Si una consulta se pasa de ese límite, MongoDB la cancela y lanza un error. No es un bug: es una medida de protección para que una consulta mal optimizada no consuma todo el servidor.
allowDiskUse: true le dice a MongoDB: “Si te quedas sin RAM, podés usar el disco como memoria de intercambio”. La consulta no se rompe, pero empieza a escribir archivos temporales en el disco duro.
A simple vista parece una solución mágica. No lo es.
Los Riesgos
Activar allowDiskUse en una consulta pesada tiene consecuencias directas:
- Rendimiento: leer y escribir en disco es órdenes de magnitud más lento que la RAM. Incluso con NVMe/SSD modernos, el impacto es brutal.
- Cuello de botella de I/O: si la consulta procesa millones de filas, MongoDB satura el disco con archivos temporales. Eso ralentiza cualquier otro servicio que intente usar el mismo almacenamiento.
- Desgaste de hardware: escrituras masivas temporales acortan la vida útil de los SSD.
No es una solución sin costo. Es un permiso para pagar el rendimiento con disco.
Cuándo Usarlo (Casos Válidos)
No está prohibido, pero tiene que ser estratégico:
- Reportes analíticos o BI: consultas pesadas que corren una vez al día, en horarios de bajo tráfico.
- Procesos background / migraciones: tareas en segundo plano donde el tiempo de respuesta no le afecte a un usuario final.
- Dashboards administrativos: vistas internas donde esperar segundos o minutos sea aceptable.
Si estás en cualquiera de esos escenarios, usalo con conocimiento de causa. Si estás sirviendo una API en tiempo real a usuarios, mejor no.
Cómo Evitarlo
La meta siempre debe ser que la consulta entre en los 100 MB de RAM sin ayuda del disco.
A. Filtrar y limitar primero
Nunca hagas operaciones pesadas como $lookup, $group o $unwind al principio si al final vas a terminar filtrado o limitando resultados.
// Mal: procesa miles de registros en RAM para quedarse con 50
db.orders.aggregate([
{ $lookup: { from: "users", localField: "userId", foreignField: "_id", as: "user" } },
{ $unwind: "$user" },
{ $sort: { createdAt: -1 } },
{ $limit: 50 }
])
// Bien: filtra y limita primero, solo procesa los 50 que necesitás
db.orders.aggregate([
{ $match: { status: "completed" } },
{ $sort: { createdAt: -1 } },
{ $limit: 50 },
{ $lookup: { from: "users", localField: "userId", foreignField: "_id", as: "user" } },
{ $unwind: "$user" },
])
B. Índices correctos
Para que $sort no consuma RAM, debe apoyarse en un índice. Seguí la regla ESR: Equality → Sort → Range.
Si $sort usa índice, su consumo de memoria pasa a ser 0 MB.
C. Proyectar solo lo necesario
Usá $project de forma temprana para eliminar campos pesados (textos largos, arrays, subdocumentos) que no necesites. Menos peso por documento = más documentos dentro de los 100 MB.
db.orders.aggregate([
{ $match: { status: "completed" } },
{ $project: { userId: 1, total: 1, createdAt: 1 } }, // Solo lo necesario
{ $group: { _id: "$userId", totalSpent: { $sum: "$total" } } }
])
Cierre
allowDiskUse: true no es malo per se. Es una red de seguridad para consultas que, por diseño, deben procesar mucho volumen. Pero si lo estás usando como parche para consultas que deberían entrar en RAM, estás tapando un problema de performance.
Antes de activar allowDiskUse, revisá el pipeline completo: filtrá primero, limitá temprano, indexá por ESR y proyectá lo mínimo. Si la consulta entra en 100 MB de RAM, el disco no se entera.