Marcadores ZFS: salvar incrementales cuando algo sale mal

¿Te fue útil?

Las snapshots son la moneda de la replicación ZFS. Las creas, las intercambias por la red con zfs send/zfs receive, y de vez en cuando dejas caer una a las 2 a.m. porque un trabajo de retención se puso “creativo”. Cuando eso ocurre, tu cadena incremental se rompe, la siguiente replicación se convierte en un envío completo y tu enlace WAN empieza a echar humo como una tostadora bajo la lluvia.

Los marcadores ZFS son la característica tranquila que hace que estos desastres vuelvan a ser aburridos. No son snapshots. No retienen datos. Son punteros diminutos al “estado del sistema de ficheros en este snapshot”, y ese puntero puede mantener vivo un flujo incremental incluso si el snapshot en sí desaparece. Los marcadores son lo que desearías haber configurado la última vez que alguien dijo: “Podemos podar agresivamente; la replicación estará bien”.

Qué son (y no son) realmente los marcadores ZFS

Un marcador ZFS es una referencia con nombre al GUID de un snapshot y a su posición en el grupo de transacciones (TXG). Piénsalo como un “punto de guardado” que dice: este dataset se veía así en el momento del snapshot X, sin mantener el namespace visible del snapshot X. Es solo metadata: no hay árbol de archivos, no hay vista montable, no puedes navegar directorios.

Los marcadores existen por una razón principal: los envíos incrementales necesitan un ancestro común en ambos lados. Normalmente, ese ancestro es un snapshot en el emisor y en el receptor. Con marcadores, el receptor puede conservar el ancestro como un marcador aun cuando el snapshot sea destruido después, y ZFS aún puede aceptar incrementales que referencien ese punto en la historia.

Los marcadores no son mágicos:

  • No permiten restaurar ficheros; no son montables.
  • No preservan bloques de datos por sí mismos; no fijan bloques como lo hacen los snapshots.
  • No pueden resucitar un flujo incremental si el emisor perdió el ancestro y no puede generar el stream correcto.

Pero para pipelines de replicación—donde el receptor suele necesitar recordar el “último punto bueno” y el emisor debe continuar desde ahí—los marcadores son la diferencia entre “el incremental continúa” y “estamos enviando 40 TB otra vez”.

Broma #1 (corta y práctica): Un marcador es como apuntar el número de habitación del hotel. Si pierdes la tarjeta (snapshot), aún puedes decir en recepción dónde se suponía que estabas.

Por qué fallan las replicaciones incrementales

El envío incremental de ZFS funciona enviando solo los bloques cambiados entre dos snapshots (o entre un snapshot y un marcador, dependiendo del lado). La parte importante es que ambos lados deben estar de acuerdo sobre el punto “desde”. Si no lo están, ZFS se niega a aplicar el stream porque no puede demostrar que dará lugar al mismo estado del sistema de ficheros.

La mayoría de las cadenas rotas son autoinfligidas. Causas comunes:

  • Poda de snapshots en el receptor: alguien borra snapshots antiguos para ahorrar espacio, sin darse cuenta de que la replicación necesita al menos un snapshot común (o un marcador) como ancla para los incrementales.
  • Poda de snapshots en el emisor: el emisor pierde el snapshot “desde” por lo que no puede generar el incremental que el receptor espera.
  • Replicación al dataset equivocado: un objetivo fue recreado, revertido o reemplazado. Ahora el “último snapshot” del receptor no está relacionado con el emisor.
  • Suposiciones inconsistentes de nombres: los scripts asumen que “el snapshot más nuevo es el más nuevo”, pero las zonas horarias, la deriva del reloj o cambios de nombres hacen que “más nuevo” no sea el que la replicación usó la última vez.

Cuando falla, los operadores suelen elegir la vía de menor resistencia: hacer un envío completo. Eso funciona—eventualmente. También consume ancho de banda, IOPS y paciencia. Los marcadores permiten mantener la ascendencia en el receptor incluso cuando borras snapshots, y eso cambia la decisión de “envío completo ahora” a “el incremental continúa según lo planeado”.

Datos interesantes y contexto

Un poco de contexto ayuda porque los marcadores son una de esas características que la gente asume que son nuevas, exóticas o “solo para grandes entornos”. No lo son.

  1. Los marcadores fueron diseñados específicamente para higiene de replicación: mantener la línea incremental sin conservar snapshots completos para siempre.
  2. Son solo metadata: crear un marcador es rápido y barato en espacio, típicamente medido como “error de redondeo” comparado con el espacio de snapshots.
  3. Los snapshots fijan bloques; los marcadores no: un marcador no evita que se libere espacio a medida que los bloques envejecen. Esto es intencional: ascendencia de replicación sin hinchar la retención.
  4. Los envíos incrementales pueden referenciar marcadores (por ejemplo, enviar desde un marcador a un snapshot nuevo), lo cual es útil operativamente cuando quieres podar snapshots en el receptor.
  5. El lado receptor puede mantener un marcador del último snapshot replicado, luego eliminar ese snapshot y aún así aceptar futuros incrementales que referencien ese punto—suponiendo que el emisor aún pueda generarlos.
  6. Los marcadores tienen su propio namespace: los listan con zfs list -t bookmark y se gestionan separadamente de los snapshots.
  7. Muchas herramientas de replicación implementaron marcadores más tarde que los snapshots, así que scripts y wrappers antiguos pueden ignorarlos a menos que se configuren explícitamente.
  8. En OpenZFS moderno, el soporte de marcadores es mainstream en plataformas comunes, pero flotas con versiones mixtas todavía tropiezan con flags de características y opciones de stream.

Mecánica central: cómo los marcadores salvan incrementales

Aquí tienes el modelo mental que aguanta bajo presión de incidentes:

  1. Un snapshot es un punto de control completo y navegable de un dataset. ZFS lo usa para calcular diferencias y para proporcionar una vista estable.
  2. Un marcador es una referencia no navegable al punto exacto en la historia de un snapshot (su GUID y metadata relacionada).
  3. Un stream incremental es una transformación del estado A al estado B. Por seguridad, el receptor debe confirmar que actualmente está en el estado A antes de aplicar la transformación.

¿Entonces cómo ayudan los marcadores cuando algo sale mal?

Supongamos que el receptor tenía el snapshot pool/fs@replica_2025-12-01, y tomaste snapshots más recientes y replicaste incrementalmente desde él. Si alguien borra @replica_2025-12-01 en el receptor, has eliminado el ancla “estado A”. Con un marcador, harías esto en su lugar:

  • Crear el marcador #replica_2025-12-01 que apunta al GUID de ese snapshot.
  • Opcionalmente eliminar el snapshot @replica_2025-12-01 para ahorrar desorden en el namespace (y posiblemente espacio si estaba fijando bloques).
  • Continuar recibiendo incrementales que refieran ese estado, porque ZFS puede validar el “desde” contra el marcador.

Restricción clave: el emisor debe seguir teniendo el snapshot “desde” (o algo equivalente) para calcular las diferencias incrementales. Los marcadores no pueden conjurar deltas de la nada. Resuelven el problema de la ascendencia en el receptor, no en el emisor.

Broma #2 (corta y relevante): La replicación ZFS es como la terapia de pareja: solo funciona si ambas partes están de acuerdo sobre lo que pasó la última vez.

Tareas prácticas con comandos (y qué significa la salida)

A continuación hay tareas reales que puedes hacer hoy. Están redactadas en la “voz del runbook” que desearía que más equipos usaran: comando, salida de ejemplo y lo que implica. Ajusta los nombres de dataset a tu entorno.

Tarea 1: Confirmar soporte de marcadores y salud del dataset

cr0x@server:~$ zpool status -x
all pools are healthy

cr0x@server:~$ zfs get -H -o value -s local,received,default all pool/fs | head -n 1
on

Interpretación: Empieza con “¿está la pool enferma?” antes de culpar a la replicación. Si la pool está degradada, podrías estar persiguiendo síntomas de rendimiento causados por resilvering o errores de checksum, no por marcadores.

Tarea 2: Listar snapshots y marcadores separadamente (no asumas que ves ambos)

cr0x@server:~$ zfs list -t snapshot -o name,used,refer,mountpoint -s creation pool/fs | tail -n 3
pool/fs@replica_2025-12-20   0B  1.20T  -
pool/fs@replica_2025-12-21   0B  1.21T  -
pool/fs@replica_2025-12-22   0B  1.22T  -

cr0x@server:~$ zfs list -t bookmark -o name,createtxg,guid -s createtxg pool/fs | tail -n 3
pool/fs#replica_2025-12-19  19548312  17084256651437522314
pool/fs#replica_2025-12-20  19591288  5721345558355301722
pool/fs#replica_2025-12-21  19634801  15884259650518200641

Interpretación: Snapshots y marcadores viven en listas diferentes. Si tu tooling solo mira snapshots, puede decirte “no hay snapshot común”, mientras ZFS podría aún tener un marcador ancla.

Tarea 3: Crear un marcador desde un snapshot existente

cr0x@server:~$ zfs bookmark pool/fs@replica_2025-12-22 pool/fs#replica_2025-12-22

Interpretación: Esta es la operación central. Estás nombrando un ancla de replicación. El nombre del marcador puede seguir tu convención de nombres de snapshots; recuerda que usa #, no @.

Tarea 4: Borrar de forma segura un snapshot antiguo tras marcarlo (poda en el receptor)

cr0x@server:~$ zfs destroy pool/fs@replica_2025-12-20

cr0x@server:~$ zfs list -t bookmark pool/fs#replica_2025-12-20
NAME                         CREATETXG  GUID
pool/fs#replica_2025-12-20   19591288   5721345558355301722

Interpretación: Puedes eliminar el snapshot mientras mantienes el puntero de ascendencia. Este es el movimiento de “salvar incrementales cuando la retención aprieta”.

Tarea 5: Verificar que aún tienes una base común en el receptor tras la poda

cr0x@server:~$ zfs get -H -o name,value type pool/fs
pool/fs	filesystem

cr0x@server:~$ zfs list -t snapshot,bookmark -o name -s name pool/fs | grep replica_2025-12-20
pool/fs#replica_2025-12-20

Interpretación: Tu ancla de cadena está presente aunque el snapshot no lo esté. Esto es lo que mantiene viables los incrementales futuros.

Tarea 6: Enviar un stream incremental usando un marcador como punto “desde”

cr0x@sender:~$ zfs send -nv -i pool/fs#replica_2025-12-20 pool/fs@replica_2025-12-22
send from pool/fs#replica_2025-12-20 to pool/fs@replica_2025-12-22 estimated size is 18.4G

cr0x@sender:~$ zfs send -w -i pool/fs#replica_2025-12-20 pool/fs@replica_2025-12-22 | ssh backup1 zfs receive -u -F pool/fs
receiving full stream of pool/fs@replica_2025-12-22 into pool/fs@replica_2025-12-22
received 18.4G stream in 00:06:12 (50.7M/sec)

Interpretación: El dry-run (-n) te dice si ZFS reconoce el marcador como base válida y estima el tamaño de la transferencia. El envío real usa -w (raw) cuando es apropiado en tu entorno; si no usas streams en crudo/encriptados, quítalo.

Tarea 7: Diagnosticar “incremental source is not earlier than destination” (un desajuste común)

cr0x@sender:~$ zfs send -i pool/fs@replica_2025-12-22 pool/fs@replica_2025-12-21
cannot send 'pool/fs@replica_2025-12-21': incremental source (pool/fs@replica_2025-12-22) is not earlier than destination (pool/fs@replica_2025-12-21)

Interpretación: Estás intentando enviar “hacia atrás” en el tiempo. Usualmente es un bug de scripting que eligió la “más reciente” incorrecta. Arregla la lógica de selección de snapshots; no forces un envío completo.

Tarea 8: Mostrar lo que el receptor cree que tiene (snapshots vs marcadores)

cr0x@backup1:~$ zfs list -t snapshot -o name -s creation pool/fs | tail -n 5
pool/fs@replica_2025-12-18
pool/fs@replica_2025-12-19
pool/fs@replica_2025-12-22

cr0x@backup1:~$ zfs list -t bookmark -o name -s createtxg pool/fs | tail -n 5
pool/fs#replica_2025-12-19
pool/fs#replica_2025-12-20
pool/fs#replica_2025-12-21

Interpretación: Este receptor no tiene los snapshots 20–21 pero sí conserva marcadores. Ese es un patrón normal de “mantener pocos puntos de recuperación, mantener muchos anclas”.

Tarea 9: Confirmar la ascendencia por GUID (cuando sospechas que replicas al objetivo equivocado)

cr0x@sender:~$ zfs get -H -o value guid pool/fs@replica_2025-12-20
5721345558355301722

cr0x@backup1:~$ zfs list -t bookmark -o name,guid pool/fs | grep replica_2025-12-20
pool/fs#replica_2025-12-20  5721345558355301722

Interpretación: GUIDs que coinciden son una fuerte evidencia de que estáis alineados en la ascendencia. Si no coinciden, no estáis hablando de la misma historia, por muy parecidos que parezcan los nombres.

Tarea 10: Convertir el “último snapshot replicado” en un marcador automáticamente

cr0x@backup1:~$ last=$(zfs list -H -t snapshot -o name -s creation pool/fs | tail -n 1)
cr0x@backup1:~$ echo "$last"
pool/fs@replica_2025-12-22

cr0x@backup1:~$ zfs bookmark "$last" "${last/@/#}"

Interpretación: Este es un patrón común: después de cada receive exitoso, marca el snapshot recibido; así tu política de retención puede eliminar snapshots sin romper la cadena.

Tarea 11: Destruir marcadores de forma segura (limpiar anclas que ya no necesitas)

cr0x@backup1:~$ zfs destroy pool/fs#replica_2025-12-19

cr0x@backup1:~$ zfs list -t bookmark -o name pool/fs | grep replica_2025-12-19 || echo "bookmark removed"
bookmark removed

Interpretación: Los marcadores pueden acumularse. Si guardas uno por cada snapshot para siempre, tu namespace acabará pareciendo un calendario explotado. Está bien podar marcadores también—hazlo con intención.

Tarea 12: Estimar el tamaño del envío antes de lanzarlo (evita envíos completos sorpresa)

cr0x@sender:~$ zfs send -nv -i pool/fs@replica_2025-12-21 pool/fs@replica_2025-12-22
send from pool/fs@replica_2025-12-21 to pool/fs@replica_2025-12-22 estimated size is 1.7G

Interpretación: El dry-run -n es una comprobación de cordura barata. Si esperas 1–3 GB y dice 1.2 TB, para y averigua por qué antes de saturar tu enlace de replicación.

Tarea 13: Comprobar la presión de espacio en el receptor y si los borrados realmente liberan espacio

cr0x@backup1:~$ zfs list -o name,used,avail,refer,mountpoint pool
NAME   USED  AVAIL  REFER  MOUNTPOINT
pool   61.2T  3.8T   192K  /pool

cr0x@backup1:~$ zfs get -o name,property,value -s local,received usedbysnapshots,usedbydataset pool/fs
NAME    PROPERTY         VALUE
pool/fs usedbysnapshots  9.4T
pool/fs usedbydataset    51.6T

Interpretación: Si borras snapshots porque te falta espacio, mide si los snapshots son realmente el culpable. Los marcadores no liberarán espacio (no retienen bloques), pero la poda de snapshots podría hacerlo—a menos que clones, holds o referencias activas mantengan bloques fijados.

Tarea 14: Recibir con precaución: entiende qué hace -F antes de usarlo con fuerza

cr0x@backup1:~$ ssh sender zfs send -i pool/fs#replica_2025-12-20 pool/fs@replica_2025-12-22 | zfs receive -u -F pool/fs

Interpretación: zfs receive -F puede revertir el dataset objetivo para que coincida con el stream entrante, destruyendo snapshots más nuevos en el receptor. A veces es exactamente lo que quieres para una réplica estricta—y a veces arruina tu carrera si el receptor también aloja snapshots locales para recuperación. Decide qué eres: réplica o backup con historial local.

Tres microhistorias del mundo corporativo

Microhistoria 1: El incidente provocado por una suposición equivocada

La suposición fue simple y errónea: “Si el receptor tiene el nombre del snapshot más reciente, los incrementales siempre funcionarán.” El script de replicación lo escribió un ingeniero capaz que nunca vivió la colisión entre políticas de retención y horarios de replicación. Usaron nombres con fechas, los ordenaron lexicográficamente, cogieron el último y lo llamaron base. Funcionó durante meses.

Entonces el equipo de almacenamiento cambió el nombre para incluir un sufijo de zona horaria porque una auditoría pidió “UTC explícito”. El emisor empezó a crear @replica_2025-12-01Z mientras el receptor aún tenía snapshots antiguos sin sufijo. El script—aún ordenando por nombre—eligió la base equivocada. ZFS hizo lo correcto y se negó a recibir el incremental con un mensaje que sonaba a argumento filosófico: el stream no coincidía con el estado actual del dataset.

Ops hizo lo que hace bajo presión: lo intentó otra vez con -F, que revirtió el receptor y borró una semana de snapshots locales “por si acaso” que no formaban parte de la replicación. Ahora tenían dos problemas: la replicación seguía fallando de forma intermitente y el helpdesk recibía peticiones de restauración que no podían satisfacer.

La solución no fue heroica. Dejaron de confiar en el nombre como verdad y empezaron a usar la ascendencia por GUID. El receptor marca el último snapshot recibido con éxito (por nombre, sí, pero verificado por GUID), y el emisor usa ese marcador como base incremental. Cuando la convención de nombres cambió de nuevo (porque las convenciones siempre cambian), la replicación no se inmutó. Siguió enviando deltas desde la historia correcta.

Microhistoria 2: La optimización que salió mal

Esta comienza con una buena idea: “Podemos ahorrar espacio podando snapshots agresivamente en el backup. Es una réplica, no un museo.” Redujeron la retención de snapshots del receptor de 30 días a 3 días. El gráfico de espacio quedó impecable. Las alertas de almacenamiento dejaron de molestar. Todos se felicitaron en una reunión que debería haber sido un correo.

Dos semanas después, una ventana de mantenimiento de red se alargó y la replicación se pausó 36 horas. Eso no es dramático por sí mismo. El drama vino de la tarea de retención: borró el último snapshot común en el receptor durante la pausa. Cuando la replicación se reanudó, el emisor intentó enviar incrementales desde un snapshot que el receptor ya no tenía. ZFS se negó a recibir, correctamente, porque no podía validar la base.

La siguiente “optimización” del equipo fue forzar envíos completos para ese dataset “hasta que se estabilice”. Los envíos completos estabilizaron el proceso: lo estabilizaron en una transferencia nocturna que coincidía con el día laboral y saturaba la ruta de escritura del array. Empezaron a llegar quejas de latencia desde la capa de bases de datos. Un sistema de backups se había convertido silenciosamente en un problema de rendimiento en producción.

El postmortem fue casi aburrido: deberían haber estado usando marcadores en el receptor todo el tiempo. Los marcadores habrían permitido podar snapshots sin romper la cadena incremental. Rehicieron la pipeline para que, tras cada receive exitoso, el receptor creara un marcador para ese snapshot y luego borrara snapshots según la retención local. La próxima vez que una pausa ocurriera, la replicación se reanudó incrementalmente como si nada. La “optimización” se convirtió en una verdadera optimización en lugar de un incidente lento.

Microhistoria 3: La práctica aburrida pero correcta que salvó el día

Otra compañía, otro ambiente. Su equipo de almacenamiento era alérgico a la sofisticación. Escribieron runbooks. Practicaron restores trimestralmente. Tenían una regla poco glamorosa: “Cada dataset replicado debe tener un ancla ‘último replicado’ legible por máquina en el receptor, independiente de la retención de snapshots.” Ese ancla era un marcador con nombre fijo, como pool/fs#last-received, actualizado en cada replicación exitosa.

Un día, un ingeniero que investigaba uso de espacio ejecutó un script de limpieza en la pestaña equivocada del historial del shell. Borró un bloque de snapshots antiguos en el receptor—exactamente el tipo de error humano que nadie admite hasta que le muestras los logs de auditoría. El equipo lo notó porque la monitorización marcó “lag de replicación en aumento” y “base incremental faltante”.

No entraron en pánico. El runbook decía: busca el marcador de nombre fijo; verifica que el GUID coincida con la base esperada; reanuda replicación desde el marcador. El marcador estaba allí, aún apuntando al último estado recibido. No necesitaron los snapshots borrados para continuar incrementales; solo necesitaban el puntero de ascendencia. La replicación se reanudó.

Hubo consecuencias, pero fueron contenidas: menos puntos de recuperación en el receptor durante esa semana y un pequeño problema de cumplimiento porque la retención no se cumplió. El sistema central permaneció saludable. No hubo colapso de la WAN, ni reenvío completo, ni misterio de “por qué la base de datos está lenta”. Esto es higiene operativa: no evitar cada error, sino hacer que los errores sean baratos.

Playbook de diagnóstico rápido

Esta es la lista “tienes 10 minutos antes de la próxima reunión y 30 minutos antes de que el enlace se sature”. Está ordenada para encontrar el cuello de botella rápido y evitar acciones destructivas.

1) Primero: ¿es realmente un problema de cadena de replicación o un problema del sistema?

cr0x@backup1:~$ zpool status -x
all pools are healthy

cr0x@backup1:~$ zfs list -o name,used,avail pool
NAME   USED  AVAIL
pool   61.2T  3.8T

Interpretación: Si la pool está degradada, casi llena o resilvering, la replicación será lenta o fallará de formas que parecen problemas de send/receive. Arregla la salud de la pool primero.

2) Segundo: ¿qué error exacto te da send/receive?

cr0x@backup1:~$ ssh sender zfs send -nv -i pool/fs@replica_2025-12-20 pool/fs@replica_2025-12-22 | zfs receive -nvu pool/fs
cannot receive incremental stream: incremental source (replica_2025-12-20) does not exist

Interpretación: Ese es un base faltante en el receptor. Aquí es donde los marcadores normalmente lo arreglan—si ya los tenías.

3) Tercero: ¿tiene el receptor un marcador que coincida con el GUID del snapshot base del emisor?

cr0x@sender:~$ zfs get -H -o value guid pool/fs@replica_2025-12-20
5721345558355301722

cr0x@backup1:~$ zfs list -t bookmark -o name,guid pool/fs | grep 5721345558355301722
pool/fs#replica_2025-12-20  5721345558355301722

Interpretación: Si tienes un marcador con el GUID correcto, probablemente puedas reanudar incrementales sin recrear snapshots en el receptor.

4) Cuarto: si los incrementales son válidos, ¿por qué va lento?

cr0x@sender:~$ zpool iostat -v 1 3
                              capacity     operations     bandwidth
pool                          alloc   free   read  write   read  write
----------------------------  -----  -----  -----  -----  -----  -----
pool                           48.1T  12.3T    210    980  92.1M  311M
  raidz2-0                     48.1T  12.3T    210    980  92.1M  311M
    sda                            -      -     30    140  12.9M  42.7M
    sdb                            -      -     28    139  12.3M  42.0M
    ...

Interpretación: Si las escrituras están saturadas en el receptor o las lecturas en el emisor, tu cuello de botella es almacenamiento, no lógica de ZFS. Si ninguno está saturado, probablemente sea red, CPU (compresión/encriptación) o trabajos de replicación serializados.

5) Quinto: valida la lógica de selección de “base” y “objetivo” en tus herramientas

cr0x@sender:~$ zfs list -H -t snapshot -o name -s creation pool/fs | tail -n 5
pool/fs@replica_2025-12-18
pool/fs@replica_2025-12-19
pool/fs@replica_2025-12-20
pool/fs@replica_2025-12-21
pool/fs@replica_2025-12-22

Interpretación: La base correcta es “el último snapshot replicado con éxito”, no “el snapshot más nuevo por nombre” ni “el de ayer”. Los marcadores facilitan persistir ese estado explícitamente.

Errores comunes, síntomas y soluciones

Error 1: Tratar marcadores como snapshots

Síntoma: Alguien intenta montar o navegar un marcador, o espera restaurar archivos desde él.

Solución: Los marcadores son solo marcadores de ascendencia. Si necesitas puntos de restauración, necesitas snapshots (o clones) en el receptor. Usa marcadores para mantener incrementales intactos mientras podas snapshots, no para sustituir snapshots por completo.

Error 2: Solo marcar en el receptor, mientras se poda en el emisor

Síntoma: El receptor tiene marcadores correctos, pero el emisor da error con “cannot send incremental: snapshot does not exist” o tu herramienta de replicación cae en envíos completos de todos modos.

Solución: El emisor debe retener suficientes snapshots para generar incrementales. Los marcadores del receptor ayudan al receptor a aceptar streams; no ayudan al emisor a calcular deltas. Alinea la retención: el emisor guarda al menos la base “último replicado” más lo que necesites para tu RPO.

Error 3: Usar zfs receive -F por reflejo

Síntoma: La replicación “vuelve a funcionar”, pero el receptor perdió snapshots más nuevos o puntos de recuperación locales. Más tarde, fallan peticiones de restore.

Solución: Decide si el objetivo es una réplica estricta (rollback aceptable) o un repositorio de backup (historial local preservado). Si es lo segundo, evita -F o usa datasets separados para réplica y backups.

Error 4: Asumir que los nombres de snapshot definen la ascendencia

Síntoma: Existen snapshots con nombres coincidentes en ambos extremos, pero los incrementales fallan con “does not match incremental source”.

Solución: Valida los GUIDs. Los nombres pueden colisionar, recrearse o aplicarse a datasets no relacionados tras rollbacks. Usa zfs get guid para confirmar la ascendencia.

Error 5: Trabajos de retención que borran primero la última base común

Síntoma: Los incrementales fallan justo después de la poda, típicamente tras una pausa o backlog en replicación.

Solución: La retención debe proteger el ancla base. Enfoque común: mantener un marcador de nombre fijo para “último recibido” y/o nunca borrar snapshots más nuevos que ese ancla hasta que el ancla avance.

Error 6: Crear demasiados marcadores sin estrategia de limpieza

Síntoma: Miles de marcadores contaminan las operaciones; listarlos tarda más; el tooling se vuelve lento o confuso.

Solución: Mantén marcadores solo por la ventana necesaria (por ejemplo, “últimos 60 anclas”) o conserva un marcador de nombre fijo “last-received” más marcadores periódicos “ancla semanal”. Podar el resto deliberadamente.

Error 7: Confundir “mejor uso de espacio” con “mejora de seguridad de replicación”

Síntoma: Podas snapshots para liberar espacio, la replicación luego se rompe y alguien sugiere “podar más”.

Solución: Separa preocupaciones: usa marcadores para la ascendencia de replicación, snapshots para puntos de recuperación, y monitoriza usedbysnapshots para entender qué fija espacio realmente.

Listas de comprobación / plan paso a paso

Este es un plan pragmático que puedes adoptar sin reescribir todo tu sistema de replicación. Asume que ya haces replicación basada en snapshots y quieres endurecerla con marcadores.

Checklist A: Introducir marcadores “último replicado” en el receptor

  1. Tras cada receive exitoso, crea/actualiza un marcador de nombre fijo que apunte al snapshot recibido.
  2. Mantén tu retención de snapshots existente para restores, pero permite que sea más agresiva porque el marcador protege la línea incremental.
  3. Asegura que tu script/herramienta de replicación pueda usar bases de marcador al generar o validar incrementales.
cr0x@backup1:~$ snap="pool/fs@replica_2025-12-22"
cr0x@backup1:~$ zfs destroy -r pool/fs#last-received 2>/dev/null || true
cr0x@backup1:~$ zfs bookmark "$snap" pool/fs#last-received
cr0x@backup1:~$ zfs list -t bookmark -o name,guid pool/fs#last-received
NAME                 GUID
pool/fs#last-received 15884259650518200641

Interpretación: Un nombre de marcador estable te da una base estable independientemente de la poda o cambios de nombres.

Checklist B: Hacer la retención consciente de la replicación (evitar cortar la rama en la que estás sentado)

  1. Identifica el snapshot correspondiente a #last-received (o mantenlo como snapshot si prefieres).
  2. Borra snapshots más antiguos primero; nunca borres el snapshot ancla hasta que hayas movido el ancla hacia delante.
  3. Poda marcadores también, pero mantén al menos el marcador de nombre fijo.
cr0x@backup1:~$ zfs list -t bookmark -o name,createtxg -s createtxg pool/fs | tail -n 5
pool/fs#replica_2025-12-20  19591288
pool/fs#replica_2025-12-21  19634801
pool/fs#replica_2025-12-22  19678110
pool/fs#last-received       19678110

Interpretación: Si #last-received sigue al más reciente, tu retención debería evitar borrar cualquier cosa “más nueva que” su estado equivalente en TXG. En la práctica, proteges los snapshots recién replicados y los marcadores ancla.

Checklist C: Procedimiento de recuperación cuando un snapshot del receptor fue borrado

  1. Detén reintentos automáticos de replicación (pueden disparar retrocesos como envíos completos).
  2. Comprueba si el receptor tiene un marcador con el GUID base.
  3. Si lo tiene, reanuda incrementales usando ese marcador como base.
  4. Si no lo tiene, decide entre: recrear una base común (raro), revertir el objetivo (peligroso) o reenvío completo (caro pero fiable).
cr0x@sender:~$ base="pool/fs@replica_2025-12-20"
cr0x@sender:~$ next="pool/fs@replica_2025-12-22"
cr0x@sender:~$ zfs send -nv -i "$base" "$next"
send from pool/fs@replica_2025-12-20 to pool/fs@replica_2025-12-22 estimated size is 18.4G

cr0x@backup1:~$ zfs list -t bookmark -o name,guid pool/fs | grep "$(ssh sender zfs get -H -o value guid "$base")"
pool/fs#replica_2025-12-20  5721345558355301722

Interpretación: Este es el enfoque de “pruébalo antes de hacerlo”. Si puedes emparejar GUIDs, normalmente puedes restaurar el flujo incremental sin opciones destructivas de receive.

Checklist D: Cordura de rendimiento para streams incrementales

  1. Estima tamaño con dry-run zfs send -nv.
  2. Comprueba throughput de lectura del emisor y de escritura del receptor (zpool iostat).
  3. Confirma la carga de CPU si usas compresión o streams encriptados.
  4. Confirma que el dataset no está siendo thrashado por otras cargas durante la replicación.

Preguntas frecuentes

1) ¿Pueden los marcadores sustituir snapshots para backups?

No. Los marcadores no contienen una vista de sistema de ficheros navegable en un punto en el tiempo y no permiten restaurar ficheros directamente. Son marcadores de ascendencia para replicación, no puntos de recuperación.

2) ¿Los marcadores consumen espacio?

Consumen una pequeña cantidad de espacio de metadata. No mantienen bloques de datos vivos como los snapshots, por lo que no provocarán el mismo comportamiento de retención de espacio.

3) Si borro un snapshot pero conservo un marcador, ¿puedo todavía hacer un envío incremental desde ese punto?

En el receptor, sí: aún puede validar incrementales que refieran esa base (marcador). En el emisor, aún necesitas un snapshot base válido (o ascendencia equivalente) para generar el delta incremental.

4) ¿Cuál es la mejor práctica operativa: un marcador por snapshot o un marcador de nombre fijo?

En producción, un marcador de nombre fijo como #last-received es la base aburrida y correcta. Algunos equipos añaden marcadores “ancla” periódicos (semanal/mensual) para seguridad, pero evita crecimiento ilimitado.

5) ¿Por qué sigo recibiendo “does not exist” cuando el receptor tiene un marcador?

Porque la referencia base del stream debe coincidir con lo que tiene el receptor. Si tu comando de envío referencia @snapname pero el receptor solo tiene #snapname, eso puede ser válido—pero solo si el metadato del stream y el emparejamiento funcionan. Verifica que el GUID coincida y considera usar explícitamente el marcador como base en el comando send cuando sea soportado.

6) ¿Los marcadores son seguros frente a rollbacks y recreaciones de dataset?

Sono seguros en el sentido de que siguen siendo referencias precisas a la historia del dataset desde el que se crearon. No son seguros en el sentido de “seguirán aplicándose a un dataset que fue destruido y recreado con el mismo nombre”. Usa comprobaciones de GUID para evitar replicar a un dataset no relacionado.

7) ¿Los marcadores ayudan si el emisor perdió snapshots por poda?

No directamente. Si el emisor ya no tiene el snapshot base necesario para calcular un incremental, puede que te veas obligado a un envío completo u otra estrategia de recuperación. Los marcadores protegen principalmente la capacidad del receptor para aceptar incrementales sin conservar snapshots antiguos.

8) ¿Debería usar zfs receive -F para “arreglar” fallos incrementales?

Sólo si el objetivo es una réplica estricta y aceptas perder snapshots más nuevos en el receptor. Si el receptor también sirve como repositorio de backups con retención local, -F puede borrar silenciosamente los puntos de recuperación que te importan.

9) ¿Cómo interactúan los marcadores con la encriptación y los envíos crudos (raw)?

Los marcadores rastrean la ascendencia independientemente de si envías streams en crudo, pero la encriptación añade restricciones: las claves y propiedades deben ser compatibles con cómo recibes. Usa dry-runs y prueba una ruta de restauración, no solo “la replicación funcionó”.

10) ¿Cuál es la versión más simple “puedo implementarlo hoy”?

En el receptor: después de cada receive exitoso, crea/actualiza #last-received apuntando al snapshot recibido. Luego ajusta la retención de snapshots para que nunca borre el snapshot replicado más reciente hasta que el marcador avance.

Conclusión

Los marcadores ZFS no son glamorosos, y ese es el punto. Son una pequeña característica de metadata que convierte una categoría de fallos de replicación en mantenimiento rutinario. Cuando los snapshots desaparecen—por accidente, por retención o porque alguien persigue espacio libre—los marcadores pueden mantener intacta tu línea incremental en el receptor, lo que a menudo significa evitar reenvíos completos costosos y la caída de rendimiento en cascada que los acompaña.

Si ejecutas replicación ZFS en producción, trata los marcadores como cinturones de seguridad: no los instalas porque planeas chocar. Los instalas porque has conocido a humanos, cron y cierres de cambio de fin de trimestre.

← Anterior
Ubuntu 24.04 «Fallo temporal en la resolución de nombres»: deja de adivinar y arregla DNS correctamente
Siguiente →
MySQL vs OpenSearch para búsqueda autohospedada: ¿vale la pena o es autolesión en un VPS?

Deja un comentario