Suena el pager, Slack se llena de mensajes y alguien dice la frase que convierte tu café en un mecanismo de afrontamiento:
“Ejecuté el delete en producción.”
Aquí es donde muere la mitología de “tenemos replicación”. La replicación es estupenda para mantenerse activo cuando falla hardware.
Es terrible para salvarte de humanos, porque replica a los humanos con sorprendente fidelidad. Lo que te salva es la
capacidad de rebobinar el tiempo: point-in-time recovery (PITR), más la disciplina operativa para hacerlo real.
Replicación vs PITR: dos promesas diferentes
Promesa de la replicación: mantenerse en línea cuando algo se rompe
La replicación trata sobre disponibilidad y escalado de lecturas. Responde a “¿y si un nodo muere?” y a veces “¿y si una zona falla?”
No responde a “¿y si escribimos basura?”. Si replicas escrituras, replicarás errores, drops de esquema y despliegues malos.
La frase dolorosa es “replication lag” (retraso de replicación). La gente la trata como una métrica de rendimiento; durante un incidente
se convierte en una herramienta de recuperación. El retraso puede darte una ventana para detener el radio de impacto. Pero es un cinturón de seguridad poco fiable:
a veces está, otras no, y no fue diseñado para el choque que estás a punto de sufrir.
Promesa del PITR: retroceder a un momento conocido bueno
PITR es una estrategia de copias de seguridad, no de replicación. El concepto es simple: tomar una copia base (snapshot completa) y luego
conservar los write-ahead logs (WAL en PostgreSQL; binlogs en MariaDB). Para recuperar, restauras la copia base y reproduces los logs
hasta justo antes del error.
PITR es lo que quieres cuando el problema es “cambiamos datos” en lugar de “perdimos un servidor”. También es lo que quieres cuando un
job de solo lectura decidió ser creativo y ejecutó un UPDATE sin cláusula WHERE.
Una idea parafraseada de John Allspaw (operaciones y resiliencia): “La fiabilidad viene de diseñar pensando en la falla, no de fingir que no ocurrirá.” (idea parafraseada)
La replicación mantiene el servicio en línea. El PITR mantiene tu carrera profesional en línea.
Modos de error humano: qué ocurre realmente
La aburrida taxonomía de desastres (que se repite)
- Borrado/actualización accidental: falta la cláusula WHERE, ID de tenant equivocado o “arreglo rápido” en una consola.
- Cambios de esquema que salen mal: eliminar una columna/tabla, orden de migración incorrecto, añadir NOT NULL antes del backfill.
- Errores en importes de datos: CSV mapeado mal, trabajo ETL usando el entorno equivocado, claves duplicadas.
- Errores de privilegios: conceder más de lo debido, revocar acceso o ejecutar mantenimiento como superusuario.
- Bugs en la aplicación: “funciona en staging” y luego el nuevo código borra filas equivocadas a gran escala.
Por qué la replicación te falla aquí
La mayoría de las replicaciones en MariaDB y PostgreSQL están diseñadas para replicar cambios comprometidos. Son lo suficientemente deterministas
para mantener las copias consistentes. Eso es genial hasta que el cambio que comprometiste es precisamente lo que quieres deshacer.
La primera trampa es psicológica: los equipos ven múltiples nodos y asumen “siempre podemos hacer failover.” El failover no deshace escrituras.
Solo te mueve a otra copia de la misma metedura de pata.
Broma #1: La replicación es como un chat grupal: todos reciben el mensaje, incluido ese mensaje embarazoso que desearías poder des-enviar.
Lo que el PITR tampoco puede hacer (para que no lo romantices)
PITR no puede reconstruir historial que no retuviste. Si no archivas WAL/binlogs de forma fiable, o los rotas con demasiada agresividad,
tu botón de rebobinado es cosmético. PITR tampoco arreglará corrupción a nivel de aplicación si no puedes identificar un punto de tiempo seguro.
Aún necesitas análisis forense: ¿cuándo empezó la mala escritura y cuándo terminó?
Mecánica de recuperación en MariaDB: binlogs, GTIDs y réplicas
Fundamentos de la replicación: qué tienes realmente en producción
MariaDB (y la familia MySQL) usa comúnmente binlogs del primario, aplicados por réplicas. Te encontrarás con replicación por archivo/posición
y replicación basada en GTID. GTID es generalmente más fácil de operacionalizar, pero no resuelve mágicamente el error humano; solo
hace que el failover y los cambios de topología sean menos propensos a errores.
Si ejecutas semi-sync, reduces la probabilidad de perder las últimas transacciones durante un failover. Eso es un control de disponibilidad/pérdida de datos,
no un control contra errores humanos.
PITR en MariaDB: copia base + reproducción de binlogs
PITR en MariaDB típicamente consiste en: tomar un backup completo (lógico con mariadb-dump o físico con herramientas como mariabackup),
luego conservar los binlogs y reproducirlos hasta justo antes de la sentencia dañina. La arista operativa es que la reproducción de binlogs puede
filtrarse y dirigirse si usas logging por filas y tienes buenos timestamps/GTIDs.
Los binlogs basados en sentencias pueden ser una trampa en la recuperación porque la reproducción puede no ser determinista dependiendo de funciones, tiempo
o sentencias no deterministas. El logging por filas es más pesado pero más predecible para la recuperación.
El patrón de réplica “retrasada” (útil, pero no es un plan)
Algunos equipos ejecutan una réplica retrasada intencionalmente para que los deletes accidentales en el primario no se apliquen de inmediato.
Esto puede salvarte si el error se detecta rápido y la ventana de retraso lo cubre.
Aún así no es un plan de recuperación completo. Las réplicas retrasadas fallan en la vida real: alguien las reinicia y se ponen al día,
o el hilo SQL se detiene y no lo notas hasta que es inútil, o el retraso es demasiado corto para incidentes de quemado lento.
Mecánica de recuperación en PostgreSQL: WAL, líneas temporales y standbys
Fundamentos de la replicación: streaming replication y su forma
La replicación por streaming de PostgreSQL envía WAL desde el primario a los standbys. Los standbys pueden ser síncronos o asíncronos.
Como en MariaDB, la replicación sirve para mantener una copia lista para tomar el relevo.
La nuance operativa: PostgreSQL tiene líneas temporales. Cuando promocionas un standby, creas una nueva línea temporal. Eso es normal,
pero importa para PITR porque tu archivo de WAL debe incluir el historial de líneas temporales, y necesitas entender qué estás restaurando.
PITR en PostgreSQL: copia base + archivado de WAL
PITR en PostgreSQL es un concepto de primera clase: una copia base más archivado continuo de segmentos WAL. La recuperación se configura
restaurando la copia base y usando una señal de recuperación más tiempo/LSN objetivo, y reproduciendo WAL desde tu archivo.
La gran ventaja: PostgreSQL tiene herramientas sólidas alrededor de la reproducción de WAL y una separación limpia entre replicación por streaming y archivado de WAL.
Pero aún tienes que construir la canalización: la fiabilidad de archive_command, la retención, la validación y las pruebas de restauración.
Broma #2: El WAL es la caja negra de tu base de datos—salvo que solo te ayude si no lo guardaste en el mismo avión.
La replicación lógica no es tu paracaídas
La replicación lógica de PostgreSQL (y sistemas similares de captura de cambios) puede ayudar a replicar tablas selectivamente y evitar
algunas categorías de dolor por cambios de esquema. No es un sustituto del PITR. La replicación lógica también transmite errores,
incluyendo meteduras de pata, y a menudo carece de la capacidad de rebobinar sin una estrategia de snapshots separada.
Qué te salva tras un error humano: matriz de decisión
Principios fundamentales: define la pregunta antes de elegir una herramienta
Tras un error humano, necesitas respuestas a tres preguntas:
- Radio de impacto: ¿qué datos cambiaron y cuánto?
- Límites temporales: ¿cuándo comenzó el cambio malo y cuándo se detectó?
- Objetivo de recuperación: ¿necesitas restaurar todo el clúster o solo un esquema/tabla/tenant?
La replicación ayuda cuando:
- Un nodo murió y necesitas hacer failover con mínima interrupción.
- Necesitas una copia de solo lectura para descargar consultas.
- Quieres capacidad de reemplazo rápida mientras reconstruyes el host fallado.
El PITR ayuda cuando:
- Los datos fueron modificados incorrectamente y necesitas ir “hacia atrás en el tiempo”.
- Se aplicaron cambios de esquema y necesitas recuperar justo antes de ellos.
- Ransomware/compromiso cifró o alteró datos en todas las réplicas.
Verdad dura: normalmente necesitas ambos
Los sistemas maduros ejecutan replicación para disponibilidad y PITR para recuperación. Si el presupuesto obliga a elegir, elige PITR primero para
sistemas donde la corrección de datos importa más que el tiempo de actividad. Eso son la mayoría de los sistemas de negocio, incluso los cuyos propietarios insisten
“podemos recrearlo”. No pueden. Solo no quieren pagar por almacenamiento.
Tareas prácticas (comandos, salidas, decisiones)
Estas son el tipo de tareas que ejecutas durante un simulacro o un incidente: rápidas, concretas y vinculadas a decisiones. Las salidas
mostradas son representativas. Tu entorno diferirá, pero el significado no.
Tarea 1 — MariaDB: confirmar que binlog está habilitado y en qué formato estás
cr0x@server:~$ mariadb -e "SHOW VARIABLES LIKE 'log_bin'; SHOW VARIABLES LIKE 'binlog_format';"
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| log_bin | ON |
+---------------+-------+
+---------------+-----------+
| Variable_name | Value |
+---------------+-----------+
| binlog_format | ROW |
+---------------+-----------+
Qué significa: existen binlogs y están basados en filas (bueno para una reproducción determinista).
Decisión: si log_bin está OFF, deja de pretender que tienes PITR; solo tienes backups.
Si binlog_format es STATEMENT, considera cambiar a ROW para recuperabilidad (tras comprobar impacto en la carga).
Tarea 2 — MariaDB: encontrar el archivo/posición actual del binlog (línea base para la cronología del incidente)
cr0x@server:~$ mariadb -e "SHOW MASTER STATUS\G"
*************************** 1. row ***************************
File: mariadb-bin.001842
Position: 987654321
Binlog_Do_DB:
Binlog_Ignore_DB:
Qué significa: esto identifica dónde está el primario “ahora”.
Decisión: regístralo en el documento del incidente; usarás archivo/posición o rangos GTID para acotar la recuperación.
Tarea 3 — MariaDB: comprobar salud de réplicas y lag
cr0x@server:~$ mariadb -e "SHOW SLAVE STATUS\G" | egrep "Slave_IO_Running|Slave_SQL_Running|Seconds_Behind_Master|Using_Gtid"
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Seconds_Behind_Master: 2
Using_Gtid: Current_Pos
Qué significa: la replicación está sana y casi al día.
Decisión: si el error humano acaba de ocurrir y el lag es pequeño, probablemente no puedas usar el lag como ventana de seguridad.
Si Slave_SQL_Running es No, trátalo como un incidente—tu “réplica de seguridad” podría estar silenciosamente muerta.
Tarea 4 — MariaDB: detener inmediatamente una réplica para preservar una copia “pre-error” (si la detectaste a tiempo)
cr0x@server:~$ mariadb -e "STOP SLAVE SQL_THREAD; SHOW SLAVE STATUS\G" | egrep "Slave_SQL_Running|Seconds_Behind_Master"
Slave_SQL_Running: No
Seconds_Behind_Master: 57
Qué significa: el hilo SQL está detenido; el hilo IO puede seguir trayendo binlogs, pero no aplicarlos.
Decisión: congela esta réplica. No reinicies el hilo SQL hasta que hayas extraído filas necesarias o armado un plan de recuperación.
Esto es una maniobra táctica, no tu restauración final.
Tarea 5 — MariaDB: localizar un evento sospechoso en binlogs por tiempo
cr0x@server:~$ mysqlbinlog --start-datetime="2025-12-30 09:55:00" --stop-datetime="2025-12-30 10:05:00" /var/lib/mysql/mariadb-bin.001842 | head -n 30
# at 456700001
#251230 9:58:12 server id 101 end_log_pos 456700321 CRC32 0x2a1b3c4d GTID 0-101-998877
BEGIN
# at 456700321
#251230 9:58:12 server id 101 end_log_pos 456701234 CRC32 0x1c2d3e4f Query thread_id=8899 exec_time=0 error_code=0
use appdb/*!*/;
SET TIMESTAMP=1767088692/*!*/;
DELETE FROM orders WHERE tenant_id=42;
/*!*/;
Qué significa: has encontrado la sentencia ofensiva y su tiempo/GTID.
Decisión: fija tu punto de parada de PITR justo antes de este GTID/tiempo; o planea una reinserción a nivel de tabla si es factible.
Si el binlog es por filas, la salida mostrará eventos de fila en lugar del SQL, lo que aún puede usarse para reproducir/omitir con precisión.
Tarea 6 — MariaDB: reproducir binlogs en una copia restaurada hasta un punto seguro
cr0x@server:~$ mysqlbinlog --stop-datetime="2025-12-30 09:58:11" /archives/mariadb-bin.* | mariadb appdb
Query OK, 0 rows affected (0.001 sec)
Query OK, 0 rows affected (0.000 sec)
Qué significa: aplicaste cambios hasta justo antes de la sentencia mala.
Decisión: valida recuentos de filas/chequeos de negocio en esta copia restaurada; luego decide si hacer cutover, extraer filas o ejecutar una fusión controlada.
Tarea 7 — PostgreSQL: confirmar que el archivado de WAL está configurado
cr0x@server:~$ psql -X -c "SHOW wal_level; SHOW archive_mode; SHOW archive_command;"
wal_level
----------
replica
(1 row)
archive_mode
--------------
on
(1 row)
archive_command
------------------------------------------------
test ! -f /wal-archive/%f && cp %p /wal-archive/%f
(1 row)
Qué significa: el archivado de WAL está activo y el archive_command es una copia simple a un directorio local.
Decisión: si archive_mode está off, no tienes PITR. Si archive_command es frágil (sin reintentos, sin alertas),
trátalo como un bug de fiabilidad y arréglalo antes de necesitarlo.
Tarea 8 — PostgreSQL: verificar que WAL realmente se está archivando (no solo está “configurado”)
cr0x@server:~$ psql -X -c "SELECT now(), last_archived_wal, last_archived_time, failed_count FROM pg_stat_archiver;"
now | last_archived_wal | last_archived_time | failed_count
-------------------------------+---------------------------+-----------------------------+--------------
2025-12-30 10:06:40.12345+00 | 000000010000003A0000009F | 2025-12-30 10:06:12+00 | 0
(1 row)
Qué significa: el archivado está funcionando recientemente; los fallos son cero.
Decisión: si failed_count aumenta, detente. Estás construyendo una falsa sensación de seguridad.
Arregla fallos del archivador primero, o tu restauración fallará por un segmento faltante y morirá a mitad de camino.
Tarea 9 — PostgreSQL: comprobar el lag de replicación durante un incidente
cr0x@server:~$ psql -X -c "SELECT application_name, state, write_lag, flush_lag, replay_lag FROM pg_stat_replication;"
application_name | state | write_lag | flush_lag | replay_lag
------------------+-----------+-----------+-----------+------------
standby-a | streaming | 00:00:00 | 00:00:00 | 00:00:02
(1 row)
Qué significa: el standby está 2 segundos retrasado en la reproducción.
Decisión: este lag no es un plan de recuperación. Si la mala escritura ya se comprometió, probablemente alcanzará al standby también.
Si necesitas un “punto de congelación”, debes actuar rápido (o confiar en PITR).
Tarea 10 — PostgreSQL: encontrar el tiempo aproximado de los cambios malos usando logs/LSN
cr0x@server:~$ psql -X -c "SELECT now(), pg_current_wal_lsn();"
now | pg_current_wal_lsn
-------------------------------+--------------------
2025-12-30 10:07:10.551+00 | 3A/9F123ABC
(1 row)
Qué significa: tienes un marcador LSN actual.
Decisión: durante la respuesta al incidente, registra LSNs junto a timestamps. Ayudan a reconciliar lo ocurrido si los relojes derivan o los logs están confusos.
Tarea 11 — PostgreSQL: restaurar una copia base y configurar el tiempo objetivo de PITR
cr0x@server:~$ sudo -u postgres bash -lc 'cat > /var/lib/postgresql/16/main/postgresql.auto.conf <
Qué significa: la recuperación tirará WAL del archivo y se detendrá en el tiempo objetivo.
Decisión: elige un tiempo objetivo justo antes de la transacción destructiva. Si no estás seguro, elige más temprano y luego vuelve a aplicar cambios conocidos-buenos cuidadosamente.
Tarea 12 — PostgreSQL: confirmar que la recuperación se detuvo donde pediste
cr0x@server:~$ psql -X -c "SELECT pg_is_in_recovery(), pg_last_wal_replay_lsn();"
pg_is_in_recovery | pg_last_wal_replay_lsn
-------------------+------------------------
f | 3A/9EFFFF00
(1 row)
Qué significa: la recuperación ha terminado (f) y tienes el último LSN reproducido.
Decisión: ejecuta consultas de validación ahora. Si los datos parecen correctos, puedes planear el cutover. Si no, vuelve a ejecutar la recuperación con un tiempo/LSN diferente.
Tarea 13 — Comprobación de sanity del almacenamiento: verificar que el volumen de archivo tiene espacio y no está lleno silenciosamente
cr0x@server:~$ df -h /wal-archive
Filesystem Size Used Avail Use% Mounted on
/dev/sdb1 500G 412G 63G 87% /wal-archive
Qué significa: el archivo WAL se está acercando a capacidad.
Decisión: si llega al 100%, el archivado de WAL falla y PITR se rompe. Implementa políticas de retención y alertas; no uses “solo añadir espacio” como estrategia.
Tarea 14 — MariaDB: verificar que la retención de binlog no comerá tu ventana de recuperación
cr0x@server:~$ mariadb -e "SHOW VARIABLES LIKE 'expire_logs_days'; SHOW VARIABLES LIKE 'binlog_expire_logs_seconds';"
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| expire_logs_days| 1 |
+-----------------+-------+
+---------------------------+--------+
| Variable_name | Value |
+---------------------------+--------+
| binlog_expire_logs_seconds| 0 |
+---------------------------+--------+
Qué significa: los binlogs expiran tras 1 día mediante expire_logs_days.
Decisión: si tu tiempo de detección de errores es mayor que un día (lo es), amplía la retención o envía binlogs a almacenamiento duradero.
De lo contrario, PITR fallará con logs faltantes justo cuando los necesites.
Guion de diagnóstico rápido
Cuando alguien grita “los datos desaparecieron”, necesitas identificar si esto es (a) un problema de replicación, (b) un problema de backups/PITR,
o (c) un bug de aplicación que aún está dañando datos. Esto es triage, no filosofía.
Primero: detener la hemorragia
- Congelar escrituras: desactiva el job, feature flag o despliegue. Si no puedes, bloquea temporalmente al usuario de la aplicación en la BD.
- Preservar evidencia: haz snapshot del volumen afectado si puedes; detén el hilo de aplicación de una réplica si la detectaste a tiempo.
- Registrar marcadores: hora actual, archivo/posición actual de binlog o WAL LSN, y quién ejecutó qué.
Segundo: decidir qué camino de recuperación es viable
- ¿Hay una réplica segura? Solo si está definitivamente pre-error (retraso intencional, detenida a tiempo o topología separada).
- ¿Es viable PITR? Confirma que el archivado de logs es continuo, que la retención cubre la ventana temporal y que tienes una copia base.
- ¿Puedes hacer reparación dirigida? A veces puedes reinsertar filas desde un snapshot/réplica sin restaurar todo.
Tercero: encontrar el cuello de botella (killer del RTO)
- Throughput de restauración: descarga de copia base + desempaquetado + rendimiento del sistema de archivos.
- Velocidad de reproducción: tasa de aplicación de WAL/binlog; comprueba si estás limitado por I/O o CPU.
- Validación y cutover: ¿puedes verificar la corrección rápidamente y cambiar el tráfico con seguridad?
Errores comunes: síntomas → causa raíz → solución
“Hicimos failover, pero el borrado está todavía allí.”
Síntoma: promocionas una réplica y las filas faltantes siguen sin estar.
Causa raíz: la replicación propagó el delete; el failover solo cambió a qué copia miras.
Solución: usa PITR o una réplica/snapshot preservada pre-error; implementa PITR con restauraciones probadas; considera réplica retardada como suplemento.
“PITR falló a mitad con archivos WAL/binlog faltantes.”
Síntoma: la recuperación se detiene con “requested WAL segment has already been removed” (PostgreSQL) o archivo binlog no encontrado (MariaDB).
Causa raíz: retención demasiado corta, pipeline de archivado fallando o archivo almacenado en el mismo disco que murió.
Solución: aumenta retención, alerta sobre fallos del archivador, almacena archivos en almacenamiento duradero separado y realiza drills periódicos de restauración que prueben continuidad.
“Restauramos, pero los datos son inconsistentes / fallan constraints.”
Síntoma: la BD restaurada no arranca limpiamente o la app falla por relaciones/constraints faltantes.
Causa raíz: mezclar dumps lógicos con reproducción física de logs incorrectamente; o restaurar esquema de un tiempo y datos de otro.
Solución: mantén coherente el PITR: la copia base debe coincidir con el stream de logs. Para restauraciones parciales, exporta/importa de forma consistente (esquema + datos) y valida.
“La réplica debía estar retrasada, pero no lo estaba.”
Síntoma: la réplica retrasada contiene el error.
Causa raíz: retraso configurado incorrectamente, réplica reiniciada y puesta al día, o monitorización que no alertó del retraso no aplicado.
Solución: monitoriza el retraso efectivo, no solo la configuración. Prueba el flujo: ¿puedes realmente detener la aplicación y extraer datos durante un simulacro?
“El disco de archivo se llenó silenciosamente; ahora no tenemos PITR.”
Síntoma: empiezan fallos de archivado; después descubres huecos.
Causa raíz: sin retención, sin alertas o archivo almacenado localmente sin planificación de capacidad.
Solución: establece políticas de retención, monitoriza espacio libre y estadísticas del archivador, y trata el archivo WAL/binlog como almacenamiento crítico de producción con SLOs.
“La restauración es demasiado lenta; el RTO es una fantasía.”
Síntoma: la restauración de base toma horas; la reproducción de WAL más tiempo; el negocio entra en pánico.
Causa raíz: backups en medios lentos, ruta de restauración no probada, cuellos de botella por cifrado/compresión o IOPS insuficientes.
Solución: mide el throughput de restauración trimestralmente, mantén backups base en caliente, ajusta almacenamiento y considera estrategias incrementales/base.
Listas de verificación / plan paso a paso
Construir una postura de recuperación que sobreviva a los humanos (plan de 90 días)
- Define RPO/RTO por base de datos: qué puedes perder y cuánto tiempo puedes estar caída.
- Implementa replicación para disponibilidad: al menos un standby/replica en un dominio de fallo separado.
- Implementa PITR:
- MariaDB: copia base física + envío y retención de binlogs.
- PostgreSQL: copia base + archivado de WAL con
pg_stat_archivermonitorizado.
- Almacenamiento separado: mueve logs de archivo fuera de los discos del primario y, idealmente, fuera del mismo radio de impacto.
- Retención según tiempo de detección: conserva logs lo suficiente para que puedas notar errores. Muchos equipos necesitan semanas, no días.
- Ejecuta drills de restauración mensualmente: una restauración PITR completa en un clúster aislado, con consultas de validación y una cronología escrita.
- Documenta el cutover: DNS/connection strings, modo solo lectura, coordinación con la aplicación y el rollback del rollback.
- Añade guardarraíles: limita acceso a consola prod, exige migraciones revisadas, protege tablas peligrosas (permisos) y usa herramientas más seguras.
Pasos de respuesta al incidente: borrado/ DROP accidental
- Detén escritores: congela el job/despliegue; evita más cambios.
- Marca la línea temporal: hora exacta de detección; identifica hora de inicio aproximada desde logs de app/auditoría.
- Elige estrategia de recuperación:
- Radio de impacto pequeño: extrae filas faltantes desde restauración PITR o réplica detenida y reinsertalas.
- Radio de impacto grande: restauración PITR completa y cutover controlado.
- Ejecuta recuperación en paralelo: una persona monta el entorno de restauración; otra valida el alcance; otra se encarga de comunicaciones.
- Valida como si lo sintieras: recuentos de filas, invariantes de negocio, pruebas de humo de la aplicación.
- Haz el cutover: cambia el tráfico, monitoriza tasas de error, mantiene el primario antiguo aislado hasta estar seguro.
Tres mini-historias corporativas desde el campo
Mini-historia 1: el incidente causado por una suposición equivocada
Una plataforma B2B mediana ejecutaba MariaDB con un primario y dos réplicas. En la documentación de ops decía “HA: sí” y todos se relajaron.
Un desarrollador ejecutó un script de limpieza que se suponía debía borrar tenants de prueba. Borró tenants reales. Rápido.
El on-call hizo lo que sugería el runbook: promocionar una réplica. Eso tomó minutos y el sitio volvió. Todos aplaudieron,
hasta que soporte reportó los mismos tenants faltantes. Claro. El delete se replicó. Acababan de hacer failover a
otra copia de la misma escena del crimen.
Tenían dumps lógicos nocturnos, pero sin retención de binlogs más allá de unas horas y sin ruta de restauración probada para un “restaurar a 10:03”.
El equipo intentó reconstruir desde eventos de aplicación y exportes parciales. Funcionó para algunos tenants, pero no para todos, y tomó días.
La corrección duradera no fue “entrenar mejor a los devs”. Fue habilitar envío de binlogs fuera del host, aumentar la retención y
ejecutar un drill de PITR mensual donde alguien elimina intencionalmente un tenant en un sandbox y lo recupera.
Mini-historia 2: la optimización que salió mal
Otra empresa ejecutaba PostgreSQL con archivado de WAL. Alguien notó que el volumen de archivo crecía rápido y “optimizeó”
la retención dejando solo un par de días. También comprimieron archivos agresivamente y los movieron a almacenamiento más lento.
Se veía genial en un dashboard de costes.
Tres semanas después, un bug de larga data empezó a corromper un subconjunto de filas. No fue dramático—suficientemente sutil
para pasar una inspección casual. Cuando finalmente se descubrió, el equipo necesitó restaurar a un punto anterior al bug.
Eso estaba fuera de la ventana de retención de WAL.
Intentaron coser una recuperación usando backups base semanales más exportes lógicos parciales, pero los datos restaurados
no se alineaban bien con las migraciones de esquema actuales. Pasaron la noche discutiendo qué “verdad” confiar: la base de datos
o las caches derivadas de la app. No es un debate divertido a las 3 a.m.
La solución fue aburrida: conservar archivos WAL más tiempo (basado en tiempo de detección, no en pánico por disco), almacenarlos
en un sistema que sostenga throughput de restauración y monitorizar el RTO real de restauración como métrica. El coste seguía importando,
pero optimizaron con datos, no con sensaciones.
Mini-historia 3: la práctica aburrida pero correcta que salvó el día
Un equipo de servicios financieros (la presión regulatoria hace maravillas por las buenas prácticas) ejecutaba PostgreSQL con replicación por streaming,
más archivado de WAL a un almacenamiento separado. Cada mes, un job programado restauraba el backup base de la semana anterior en
un entorno aislado y ejecutaba un conjunto de “invariantes”: sumas de saldos correctas, claves foráneas coincidentes y una muestra de
journeys de clientes que se podían reproducir.
Una tarde, un ingeniero aplicó una migración que eliminó un índice y luego ejecutó un update que accidentalmente afectó muchas más filas de las previstas.
El sitio se mantuvo activo—la replicación estaba bien—pero los datos visibles al cliente eran incorrectos. Congelaron escrituras de inmediato.
Como habían hecho drills de restauración, ya conocían el tiempo de restauración, los comandos y qué ruta de almacenamiento era la más rápida.
Restauraron a cinco minutos antes de la migración, validaron invariantes y luego usaron una reproducción controlada de eventos de aplicación
para una ventana estrecha. El cutover fue tenso pero limpio.
Nadie recibió una ovación de pie. Ese es el punto. La recompensa por la corrección es un canal de incidentes tranquilo y que todos se vayan a dormir.
Hechos y contexto histórico que vale la pena conocer
- El WAL de PostgreSQL tiene herencia de décadas: la idea central del write-ahead logging es anterior a muchos productos de backup “modernos”.
- MariaDB se bifurcó de MySQL en 2009 tras la adquisición de Sun por Oracle; esa historia moldeó la confianza empresarial y las herramientas del ecosistema.
- Los formatos de binlog de MySQL/MariaDB evolucionaron: el logging basado en sentencias apareció primero; el basado en filas se volvió la opción más segura para recuperación determinista.
- Las líneas temporales en PostgreSQL importan: cada promoción crea una nueva línea temporal; olvidar archivos de historial puede romper restauraciones de forma confusa.
- La replicación nunca se diseñó como deshacer: ambos sistemas priorizan la consistencia de copias, no la reversibilidad de transacciones.
- “Replicación síncrona” no significa “seguro contra humanos”: reduce pérdida de datos en crash/failover, no la corrupción lógica.
- La replicación lógica es selectiva pero no viaje en el tiempo: es excelente para migraciones y replicación parcial, no para rebobinar errores.
- Muchos outages son fallos de detección: el error humano suele ocurrir minutos u horas antes de ser notado; la retención debe coincidir con la realidad.
Preguntas frecuentes
1) Si tengo replicación, ¿aún necesito PITR?
Sí. La replicación maneja hardware y algunos escenarios de failover. PITR maneja escrituras malas, errores de esquema y compromisos que se propagan.
Si solo eliges uno para una BD crítica, elige PITR y acepta cierto riesgo de downtime.
2) ¿Puede una réplica retardada reemplazar el PITR?
No. Es un suplemento útil: una forma rápida de obtener filas faltantes o preservar una copia pre-error. Pero puede fallar silenciosamente,
y no cubrirá corrupción de quemado lento fuera de su ventana de retraso.
3) ¿Cuál es la mayor diferencia operativa entre la recuperación de MariaDB y PostgreSQL?
PostgreSQL tiene un flujo de trabajo PITR muy explícito construido alrededor de copias base y archivado de WAL con introspección fuerte (como pg_stat_archiver).
PITR en MariaDB es potente pero más “arma tu propia aventura”: backups físicos más gestión de binlogs y reproducción cuidadosa.
4) ¿Debo usar dumps lógicos para PITR?
Los dumps lógicos son geniales para portabilidad y restauraciones a nivel de tabla, pero por sí solos no son PITR. PITR necesita una base consistente + stream de logs.
Usa dumps lógicos como complemento, no como columna vertebral, salvo que tu tamaño de datos y RTO lo permitan.
5) ¿Cuánto tiempo debería retener WAL/binlogs?
Basa la retención en el tiempo de detección, no en la comodidad. Si tu organización descube issues días después, conserva al menos semanas de logs.
Además asegúrate de poder costear el tiempo de restauración para esa ventana.
6) ¿Cuál es la mejor forma de saber que mi RTO es real?
Realiza restauraciones programadas y mide: tiempo para obtener backup, tiempo para restaurar, tiempo para reproducir logs, tiempo para validar,
tiempo para cutover. Si no mides, tu RTO es un deseo.
7) ¿Puedo hacer PITR “justo antes de una transacción” de forma fiable?
Usualmente sí, pero la precisión varía. PostgreSQL puede apuntar a timestamps y también puede trabajar con objetivos LSN. MariaDB puede reproducir hasta un datetime
y a menudo hasta límites GTID dependiendo de herramientas y formato de logging. Cuanto más registres y más practiques, más precisa será la recuperación.
8) ¿Y si solo quiero restaurar una tabla o un tenant?
El patrón común es: restaurar PITR en un entorno aislado, extraer las filas o la tabla necesaria y luego fusionar en prod con cuidado.
Esto es más lento que un cutover completo pero evita un rollback global por errores localizados.
9) ¿El cifrado/compresión de archivos afecta la recuperación?
Sí. Puede aplastar el throughput de restauración y convertir “tenemos backups” en “tenemos backups lentos”. Si comprimes, prueba la velocidad de restauración bajo carga.
No optimices costes de almacenamiento haciendo la recuperación imposible.
Conclusión: siguientes pasos que puedes hacer esta semana
Si tomas una lección operativa del debate MariaDB vs PostgreSQL, que sea esta: la replicación no es un botón de rebobinado.
Ambas bases pueden recuperarse muy bien tras un error humano—pero solo si tratas al PITR como una característica de producción, no como una casilla marcada.
- Elige una ventana de recuperación realista (RPO/RTO) y alinea la retención de logs al tiempo de detección.
- Prueba que el PITR funciona: haz un drill de restauración completo de extremo a extremo y mídelo.
- Instrumenta la canalización de archivado: alerta sobre fallos en archivado de WAL/binlog y sobre capacidad de almacenamiento de archivos.
- Escribe el runbook de “detener la hemorragia”: congelar escrituras suele ser la diferencia entre un arreglo de 10 minutos y una reconstrucción de una semana.
- Mantén la replicación para uptime, pero deja de venderla internamente como protección contra errores. No lo es.
El día que necesites esto, no tendrás tiempo para convertirte en la persona que lo sabe. Construye el músculo ahora, mientras nadie mira.