Diagnóstico y Resolución de Problemas Comunes de Retraso en la Replicación de MongoDB

Navega por las complejidades del retraso en la replicación de MongoDB con esta guía completa. Aprende a identificar, diagnosticar y resolver problemas comunes que comprometen la consistencia de datos y la alta disponibilidad en tus conjuntos de réplicas. El artículo cubre desde la comprensión del oplog y la detección del retraso con `rs.status()` hasta soluciones prácticas para tamaño insuficiente del oplog, cuellos de botella de red, limitaciones de recursos e índices faltantes. Equípate con estrategias accionables y mejores prácticas para mantener un entorno MongoDB saludable, eficiente y resiliente.

Diagnóstico y Resolución de Problemas Comunes de Retraso en la Replicación de MongoDB

El retraso en la replicación de MongoDB no es solo un número en un panel de control. Cambia la forma en que se comporta tu aplicación. Un usuario actualiza un perfil, otra solicitud lee desde un secundario y el valor antiguo regresa. Un nodo falla, pero el mejor secundario aún está atrasado, por lo que la conmutación por error toma más tiempo de lo esperado. Una consulta de informes llega al miembro equivocado y de repente el conjunto de réplicas parece saludable excepto por un secundario que sigue alejándose del primario.

La forma útil de pensar sobre el retraso en la replicación es simple: el primario está produciendo entradas de oplog más rápido de lo que uno o más secundarios pueden obtenerlas y aplicarlas. La solución depende de qué lado de esa oración sea cierto en tu entorno. A veces el primario está escribiendo demasiado en ráfagas. A veces el secundario tiene poca potencia. A veces la red es lenta. A veces el retraso es intencional porque el miembro está configurado con secondaryDelaySecs. Tu primer trabajo es separar esos casos antes de hacer cambios.

Comienza con la Forma Real del Retraso

No empieces redimensionando el oplog o reiniciando mongod. Primero averigua si el retraso es constante, irregular, limitado a un miembro o afecta a todos los secundarios.

En mongosh, comienza con:

rs.status()

Observa los campos stateStr, optimeDate, lastHeartbeatMessage y health de cada miembro. Si un secundario está atrasado y los demás están al día, probablemente tengas un problema específico del miembro: disco, CPU, lecturas locales, mantenimiento local o una mala ruta de red. Si todos los secundarios están atrasados, examina más a fondo el volumen de escritura del primario, el rendimiento de red desde el primario o una operación inusualmente grande.

Para una verificación rápida de la ventana del oplog, ejecuta:

rs.printReplicationInfo()

La ventana del oplog te indica cuánto tiempo cubre el oplog actual. No dice que la replicación sea saludable. Dice qué tan atrás puede estar un secundario antes de arriesgarse a necesitar una sincronización inicial. Si tu ventana de oplog es de 6 horas y tus ventanas de mantenimiento suelen durar 8 horas, tienes un riesgo operativo real incluso cuando el retraso actual es cero.

Para los secundarios, esto también es útil:

rs.printSecondaryReplicationInfo()

En ejemplos antiguos puedes ver rs.printSlaveReplicationInfo(). La redacción más nueva usa "secondary", pero los ayudantes de shell antiguos y publicaciones de blog antiguas pueden seguir usando "slave". Los campos importan más que el nombre.

Si quieres un pequeño script para un shell en vivo, compara la hora óptima del primario con cada secundario:

const status = rs.status();
const primary = status.members.find(m => m.stateStr === "PRIMARY");

status.members
  .filter(m => m.stateStr === "SECONDARY")
  .forEach(m => {
    const lagSeconds = (primary.optimeDate - m.optimeDate) / 1000;
    print(`${m.name}: ${lagSeconds}s detrás del primario`);
  });

Trátalo como una instantánea, no como un diagnóstico. Un secundario que está 20 segundos atrasado durante una importación por lotes puede estar bien si se pone al día rápidamente. Un secundario que siempre está 20 segundos atrasado durante el tráfico normal merece atención.

Verifica si el Retraso es Intencional

Antes de perseguir un incidente falso, inspecciona la configuración del conjunto de réplicas:

rs.conf()

Un miembro retrasado está configurado para seguir al primario por diseño. En la configuración moderna de MongoDB, busca secondaryDelaySecs en un miembro. Ese miembro es útil para algunos escenarios de recuperación porque puede preservar una vista más antigua de los datos por un período corto. No debe usarse para lecturas frescas, y su retraso esperado debe excluirse de las alertas normales de retraso.

El error que veo en operaciones reales es alertar sobre cada miembro retrasado como si estuviera roto. Alerta sobre el retraso más allá del retraso configurado. Si un miembro está retrasado 1 hora y muestra 1 hora y 5 minutos de retraso, el retraso real es de aproximadamente 5 minutos.

Cuando la Ventana del Oplog es Demasiado Pequeña

El oplog es una colección con tope en la base de datos local. Los secundarios lo leen y aplican las operaciones en orden. Si un secundario se queda tan atrás que el primario ya no tiene las entradas de oplog que necesita, la recuperación ordinaria ya no es posible. El miembro generalmente necesita una sincronización inicial o una restauración desde una copia de seguridad adecuada.

Por eso la ventana del oplog importa. Debe cubrir más que tu tiempo de inactividad esperado, mantenimiento, interrupción de red y ráfagas de escritura máxima. No hay un tamaño de oplog "correcto" universal. Un clúster tranquilo puede mantener días de historial en un oplog pequeño. Un clúster ocupado con actualizaciones pesadas puede consumir el mismo tamaño en un período corto.

Si la ventana del oplog se está reduciendo durante el tráfico pico, auméntala antes de la próxima ventana de mantenimiento. En versiones compatibles de MongoDB, usa replSetResizeOplog en lugar de eliminar y recrear local.oplog.rs. Eliminar el oplog en un miembro del conjunto de réplicas es una maniobra de recuperación de alto riesgo, no un paso de ajuste normal.

Ejecuta el comando de redimensionamiento en el miembro cuyo oplog deseas redimensionar:

use admin
db.adminCommand({ replSetResizeOplog: 1, size: 10240 })

El valor de size está en megabytes. Un valor de 10240 significa aproximadamente 10 GB. Redimensiona cada miembro según sea necesario. En entornos administrados como MongoDB Atlas, usa la ruta de configuración compatible de la plataforma en lugar de asumir control directo del sistema de archivos o del proceso.

Después de redimensionar, verifica la nueva ventana bajo carga de escritura real. Un oplog más grande reduce la posibilidad de caerse del oplog, pero no hace que un secundario lento aplique operaciones más rápido.

Cuando un Secundario es Lento

Si solo un secundario se retrasa, inicia sesión en ese host y observa los síntomas normales del sistema. A menudo se culpa a MongoDB por lo que en realidad es saturación de disco.

Usa herramientas como:

iostat -xz 1
vmstat 1
top
mongostat --host secondary.example.com:27017
mongotop --host secondary.example.com:27017

Alta utilización de disco, tiempos de espera altos o una cola de E/S larga generalmente significan que el secundario no puede escribir lo suficientemente rápido. Esto puede ocurrir cuando se usa un tipo de instancia más barato para los secundarios, cuando EBS o el almacenamiento en red tienen un rendimiento aprovisionado más bajo, o cuando las copias de seguridad y las instantáneas del sistema de archivos se ejecutan al mismo tiempo que las escrituras máximas de la aplicación.

La CPU también puede importar, especialmente con compresión, cifrado, movimientos de documentos, mantenimiento de índices o una carga de trabajo con muchas actualizaciones pequeñas. La presión de memoria se manifiesta como fallos de página, cambios de caché y un secundario que sigue leyendo desde el disco mientras intenta aplicar entradas de oplog.

La solución práctica suele ser aburrida: proporciona al secundario almacenamiento y CPU comparables al primario, reduce el trabajo competidor en ese host o mueve las lecturas pesadas a otro lugar. Un miembro del conjunto de réplicas no es capacidad de informes gratuita. Aún tiene que mantenerse al día con la replicación.

Cuando las Lecturas en Secundarios Causan el Problema

El escalado de lecturas con secundarios es útil, pero es fácil exagerar. Una consulta de panel que escanea una colección grande puede competir con la aplicación del oplog. El secundario puede seguir aceptando lecturas, pero la replicación se retrasa porque la misma CPU, caché y disco se están utilizando para consultas de usuario.

Verifica el perfilador y las operaciones actuales en el miembro retrasado:

db.currentOp({ active: true })

Si ves lecturas largas, trabajos de agregación o scripts de mantenimiento, decide si ese secundario debería realmente servir esa carga de trabajo. Para informes, un secundario oculto o dedicado puede ser una mejor opción. Para lecturas de aplicación, establece maxStalenessSeconds para que el controlador evite secundarios que están demasiado atrasados.

Para rutas críticas de consistencia, usa lecturas del primario. Ejemplos incluyen estado de inicio de sesión, confirmación de pago, cambios de contraseña, configuraciones de cuenta y cualquier cosa donde un usuario espere leer su propia escritura inmediatamente. Las lecturas secundarias son mejores para datos donde una breve desactualización es aceptable.

Cuando el Primario Produce Ráfagas

Las escrituras grandes pueden hacer que los secundarios saludables parezcan rotos. Importaciones masivas, actualizaciones amplias de múltiples documentos, limpieza de TTL, eliminaciones grandes y cambios de índices pueden producir una ráfaga de actividad de oplog que toma tiempo en aplicarse.

Busca operaciones recientes en el primario:

db.currentOp({ active: true })

También verifica despliegues de aplicaciones, trabajos de reparación de datos, rellenos y tareas programadas. El retraso de replicación que comienza exactamente a las 02:00 a menudo no es misterioso. Es un trabajo por lotes.

Cuando controlas el trabajo, divídelo en fragmentos más pequeños. Por ejemplo, actualiza documentos por rangos de _id, pausa entre lotes y observa el retraso mientras el trabajo se ejecuta. Con bulkWrite, las escrituras desordenadas pueden mejorar el rendimiento, pero el manejo de errores debe ser explícito porque las fallas pueden ser parciales. El objetivo no es siempre hacer que el primario termine lo más rápido posible. El objetivo es permitir que el conjunto de réplicas absorba el trabajo sin perder su margen de recuperación.

Índices y Aplicación del Oplog

En un conjunto de réplicas normal, los índices se replican. Si los índices difieren entre miembros debido a trabajo manual, mantenimiento fallido o un nodo que fue restaurado incorrectamente, un secundario puede volverse dolorosamente lento al aplicar actualizaciones y eliminaciones. La operación del oplog puede necesitar encontrar un documento, y sin el índice esperado, el secundario puede hacer mucho más trabajo del que hizo el primario.

Compara las definiciones de índices en las colecciones afectadas:

db.orders.getIndexes()

Ejecuta el mismo comando en el primario y en el secundario retrasado. Si difieren, descubre por qué antes de hacer más cambios. Reconstruir un índice grande puede generar carga, así que planifícalo durante un período tranquilo o reconstruye el miembro desde una fuente conocida si la divergencia es amplia.

No uses consejos antiguos que dicen que las construcciones de índices en segundo plano resuelven todas las preocupaciones de replicación. El comportamiento de construcción de índices de MongoDB ha cambiado entre versiones, y la elección operativa correcta depende de tu versión y topología. Usa la documentación actual del servidor para la versión exacta que ejecutas.

Los Problemas de Red Generalmente Son Visibles en Otro Lugar

El retraso de red tiende a manifestarse como latidos inestables, errores intermitentes o bajo rendimiento entre hosts o regiones específicas. Las comprobaciones básicas aún ayudan:

ping primary.example.com
traceroute primary.example.com

Pero una latencia de ping baja no prueba suficiente ancho de banda. La replicación puede estar limitada por el rendimiento, la pérdida de paquetes, la inspección de firewall, los enlaces entre regiones o la red compartida ruidosa. Si el retraso aparece solo para un secundario remoto, compáralo con un secundario en la misma región que el primario. Si los miembros de la misma región están bien y el miembro remoto está atrasado, la topología puede estar pidiendo demasiado al enlace.

Para conjuntos de réplicas entre regiones, sé honesto sobre la compensación. Pueden ayudar con la recuperación ante desastres, pero están más expuestos a límites de latencia y ancho de banda. Si el miembro remoto está destinado a lecturas, usa controles de desactualización y prueba el comportamiento de conmutación por error en lugar de asumir que se comportará como un secundario local.

Ten Cuidado con los Consejos de Reinicio y Resincronización

Reiniciar mongod puede resolver un problema transitorio, pero también puede empeorar un incidente si el nodo estaba cerca de caerse del oplog. Antes de reiniciar, verifica la ventana del oplog y el retraso actual. Si el nodo necesita dos horas para ponerse al día y la ventana del oplog es solo de tres horas durante el tráfico pico, un reinicio prolongado puede dejarte con una sincronización inicial en lugar de una recuperación.

La sincronización inicial es una opción de reparación válida cuando un secundario está obsoleto, corrupto o carece del historial de oplog requerido. También es costosa. Copia datos, construye índices y consume recursos de red y disco de las fuentes de sincronización. En producción, prefiere agregar o reconstruir un miembro a la vez para que el conjunto de réplicas mantenga suficientes miembros votantes y portadores de datos para tolerar fallos.

Si un miembro está tan atrasado que no puede ponerse al día, toma una ruta basada en copia de seguridad o instantánea fresca que coincida con tus estándares operativos. No elimines un directorio de datos porque una lista de verificación lo diga. Confirma que el miembro es desechable, confirma que el conjunto de réplicas puede tolerar la reconstrucción y confirma que tienes suficiente ventana de oplog o una fuente de sincronización inicial confiable.

Alerta sobre lo que Importa a Usuarios y Operadores

Una buena alerta no es "el retraso de replicación es mayor a 1 segundo" para cada sistema. Algunas aplicaciones pueden tolerar 30 segundos en lecturas de análisis. Otras no pueden tolerar lecturas desactualizadas en el estado de la cuenta. Los umbrales de alerta deben reflejar el caso de uso.

Las alertas útiles incluyen:

  • Retraso de replicación por encima de la tolerancia de la aplicación durante un período sostenido.
  • Ventana del oplog por debajo del intervalo de mantenimiento o recuperación más largo esperado.
  • Un secundario en estado RECOVERING, STARTUP2 o no saludable por más tiempo del esperado.
  • Saturación de E/S de disco en cualquier miembro portador de datos.
  • Fallos de latido o errores de red entre miembros.

Los paneles deben mostrar el retraso junto con el volumen de escritura, la latencia de disco, la CPU, la presión de memoria y el rendimiento de red. El retraso por sí solo te dice que hay un problema. Los gráficos vecinos generalmente te dicen cuál problema.

Un Orden de Triage Práctico

Cuando estés de guardia, usa este orden:

  1. Confirma qué miembros están retrasados con rs.status().
  2. Verifica si algún retraso es intencional debido a secondaryDelaySecs.
  3. Verifica la ventana del oplog con rs.printReplicationInfo().
  4. Compara el retraso con picos de escritura, trabajos por lotes y despliegues recientes.
  5. Inspecciona el disco, CPU, memoria y carga de consultas locales del secundario retrasado.
  6. Verifica errores de red y latencia entre los miembros afectados.
  7. Decide si el miembro puede ponerse al día, necesita que se le retire carga, necesita más recursos o debe ser reconstruido.

El mejor resultado generalmente no es un comando dramático. Es encontrar el cuello de botella y eliminarlo sin crear divergencia de datos. El retraso de replicación de MongoDB es manejable cuando lo tratas como una señal de capacidad y topología, no como una falla genérica de MongoDB.