Files
dtic-DIIAA/docs/ambito/dtic-ADN/A01.P012_Pipeline-Drones-Intercomunicacion.md
Ricardo Monla 892019fa22 [A04.P005] DASUTEN: Pipeline refactorizado + Documentación + Informes
Refactorización del pipeline de backups DASUTEN:

1. Flujo simplificado (un solo destino):
   - Backup directo a X:\ (share srv-ns8)
   - Eliminado paso intermedio E:\BK_SQL\sysdasuten\
   - Reducido de 3 ubicaciones a 2 (X: → /var/tmp/)

2. Módulo común DasuExecutor (lib/dasu_executor.rb):
   - Centraliza ejecución PowerShell en dasu-sql4
   - Centraliza verificación de archivos en fenix
   - Elimina código repetido en 6 drones (-52% líneas)

3. Drones refactorizados:
   - dasuten_exportar_srvv-fenix.rb (FULL → X:)
   - dasuten_exportar_diferencial_srvv-fenix.rb (DIF → X:)
   - dasuten_transferir_srvv-fenix-srv-ns8.rb (simplificado)
   - dasuten_restaurar_dasu-sql4.rb (usa DasuExecutor)
   - dasuten_restaurar_diferencial_dasu-sql4.rb (usa DasuExecutor)
   - dasuten_download_drive-dasu-sql4.rb (usa DasuExecutor)
   - dasuten_verificar-integridad_dasu-sql4.rb (usa DasuExecutor)

4. Integración BKPs mantenida:
   - T8-T15: Tareas individuales
   - C7: dasuten_full (pipeline semanal)
   - C8: dasuten_diferencial (pipeline diario)

5. Documentación actualizada:
   - A04.P006_Backups-DASUTEN.md: flujo simplificado + módulo común
   - Lecciones aprendidas agregadas

6. Nuevos archivos:
   - Informes técnicos DASUTEN (docs/informes/2026-04_DASUTEN-migracion/)
   - A01.P011/P012: Documentación de drones y pipeline
   - proc_linux.rb, ops.rb: Utilitarios del ecosistema

7. Scripts legacy (para referencia):
   - orquestador_pipeline.rb, orquestador_pipeline_diferencial.rb
   - dasuten_upload_srv-ns8-drive.rb
2026-04-14 20:06:06 -03:00

23 KiB

A01.P012 — Pipeline de Drones para Backup y Restauración de DASUTEN

Estado: 🟢 Pipeline implementado (2026-04-14) Pertenece a: A01 — Ecosistema ADN Relacionado: A01.P009 — Evolución Dron ADN Caso de Uso: A04.P005 — DASUTEN sin DC Fecha: 2026-04-14 Responsable: Lic. Ricardo MONLA Versión: 2.0 — Con verificación de consistencia (DBCC CHECKDB) de BD


📈 Progreso

Fase 0:    ██████████ 100%  Diseño de Arquitectura de Pipeline ✅
Fase 1:    ██████████ 100%  Drones Atómicos Fragmentados ✅
Fase 1.5:  ██████████ 100%  Orquestador con Intercomunicación JSON ✅
Fase 1.7:  ██████████ 100%  Implementación Real — Pipeline DASUTEN ✅ (2026-04-14)
Fase 1.8:  ██████████ 100%  Verificación de Consistencia (DBCC CHECKDB) ✅
Fase 1.9:  ██████████ 100%  Pipeline Diferencial (Refresco diario) ✅
Fase 2:    ░░░░░░░░░░   0%  Persistencia en Bitácora — Outputs en DB
Fase 3:    ░░░░░░░░░░   0%  Reintentos Automáticos — Retry con backoff
Fase 4:    ░░░░░░░░░░   0%  Condicionales — Skip si output indica "ya hecho"

📋 Resumen Ejecutivo

Este plan documenta el Pipeline de Drones para Backup y Restauración de DASUTEN — una implementación real donde múltiples drones atómicos se encadenan para formar el flujo de refresco de base de datos (Fase 8b), comunicándose entre sí mediante archivos JSON que actúan como medio de intercambio de contexto.

Caso de uso: Refresco de la base de datos sysdasuten desde srvv-fenix (producción) hasta dasu-sql4 (nuevo servidor), pasando por Google Drive como intermediario.

Problema resuelto: Transferir 1.3 GB de backup entre servidores sin dependencia de Tailscale inestable, usando Google Drive como puente y verificando integridad con DBCC CHECKDB.

Solución: Un orquestador que:

  1. Lanza 6 drones atómicos secuencialmente
  2. Lee el output JSON de cada dron completado
  3. Inyecta el contexto en el siguiente dron
  4. Decide continuar o abortar según el resultado

Convención de Nombres (desde 2026-04-14)

dasuten_<verbo>_<origen-destino>.rb

Ejemplos:
- dasuten_exportar_srvv-fenix.rb     (acción en nodo específico)
- dasuten_transferir_srvv-fenix-srv-ns8.rb  (transferencia entre nodos)
- dasuten_upload_srv-ns8-drive.rb    (upload desde nodo a Drive)

🎯 Objetivo

Implementar un Pipeline de Drones para el refresco de BD de DASUTEN que permita:

  1. Fragmentar el flujo de backup en 6 drones atómicos independientes (1 dron = 1 paso)
  2. Intercomunicar drones mediante archivos JSON estandarizados (/tmp/dron_*_output.json)
  3. Orquestar el refresco completo con un solo comando desde srv-ns8
  4. Registrar trazabilidad de cada paso en la Bitácora Web
  5. Permitir re-ejecución de pasos individuales sin re-correr todo el pipeline
  6. Verificar integridad de la BD restaurada con DBCC CHECKDB

🏗️ Arquitectura del Pipeline

Diagrama de Flujo (6 Drones Atómicos)

┌──────────────────────────────────────────────────────────────────────────┐
│                    ORQUESTADOR (orquestador_pipeline.rb)                 │
│                                                                          │
│  ┌────────────────────────────────────────────────────────────────────┐ │
│  │  Contexto Compartido (Hash Ruby)                                   │ │
│  │  - backup_ruta: ruta del archivo .bak                              │ │
│  │  - drive_ruta: URL de Google Drive                                 │ │
│  │  - estado_integridad: resultado de DBCC CHECKDB                    │ │
│  │  - duracion_total: suma de duraciones de drones                    │ │
│  └────────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────────┘
         │
         │ 1. Lanza dron
         ▼
┌─────────────────┐
│  DRON:          │  Nodo: srvv-fenix
│  exportar       │  Tarea: BACKUP DATABASE WITH COMPRESSION
│                 │  Output: /tmp/dron_export_output.json
└─────────────────┘
         │
         │ 2. Orquestador lee JSON, extrae backup_ruta
         ▼
┌─────────────────┐
│  DRON:          │  Nodo: srv-ns8
│  transferir     │  Tarea: smbclient //fenix/BK_SQL → /var/tmp/
│                 │  Output: /tmp/dron_transfer_output.json
└─────────────────┘
         │
         │ 3. Orquestador lee JSON, extrae backup_local
         ▼
┌─────────────────┐
│  DRON:          │  Nodo: srv-ns8
│  upload         │  Tarea: rclone copy → Google Drive
│                 │  Output: /tmp/dron_upload_output.json
└─────────────────┘
         │
         │ 4. Orquestador lee JSON, extrae drive_ruta
         ▼
┌─────────────────┐
│  DRON:          │  Nodo: dasu-sql4
│  download       │  Tarea: Invoke-WebRequest ← Google Drive
│                 │  Output: /tmp/dron_download_output.json
└─────────────────┘
         │
         │ 5. Orquestador lee JSON, extrae backup_local
         ▼
┌─────────────────┐
│  DRON:          │  Nodo: dasu-sql4
│  restaurar      │  Tarea: RESTORE DATABASE (sin CHECKDB)
│                 │  Output: /tmp/dron_restore_output.json
└─────────────────┘
         │
         │ 6. Orquestador lee JSON, decide verificar
         ▼
┌─────────────────┐
│  DRON:          │  Nodo: dasu-sql4
│  verificar      │  Tarea: DBCC CHECKDB + validaciones
│                 │  Output: /tmp/dron_verify_output.json
└─────────────────┘
         │
         ▼
    ✅ PIPELINE COMPLETADO (6 drones)

🧩 Drones Atómicos del Pipeline (6 drones)

Cada dron es independiente, autocontenido y auto-bitácorado.

Dron 1: dasuten_exportar_srvv-fenix.rb

Campo Valor
Nodo srvv-fenix
Tarea Generar backup comprimido en SQL Server
Comando BACKUP DATABASE sysdasuten TO DISK = ... WITH COMPRESSION
Output File /tmp/dron_export_output.json
Output Clave backup_ruta

Output JSON:

{
  "paso": "export_complete",
  "backup_ruta": "E:\\BK_SQL\\sysdasuten_compressed_ADN.bak",
  "nodo": "srvv-fenix",
  "duracion": 45.2,
  "timestamp": "2026-04-14T10:00:00-03:00",
  "siguiente_paso": "transferir"
}

Dron 2: dasuten_transferir_srvv-fenix-srv-ns8.rb

Campo Valor
Nodo srv-ns8
Tarea Descargar backup desde srvv-fenix vía SMB
Comando smbclient //10.0.10.200/BK_SQL -c "get sysdasuten_compressed_ADN.bak"
Output File /tmp/dron_transfer_output.json
Output Clave backup_local

Output JSON:

{
  "paso": "transfer_complete",
  "backup_local": "/var/tmp/sysdasuten_compressed_ADN.bak",
  "nodo": "srv-ns8",
  "duracion": 120.5,
  "timestamp": "2026-04-14T10:02:00-03:00",
  "siguiente_paso": "upload"
}

Dron 3: dasuten_upload_srv-ns8-drive.rb

Campo Valor
Nodo srv-ns8
Tarea Subir backup a Google Drive vía rclone
Comando rclone copy /var/tmp/... rmonla-GDrive:drive_bkps-dasu/...
Output File /tmp/dron_upload_output.json
Output Clave drive_ruta

Output JSON:

{
  "paso": "upload_complete",
  "drive_ruta": "rmonla-GDrive:drive_bkps-dasu/sysdasuten_compressed_ADN_20260414_100500.bak",
  "drive_url": "https://drive.google.com/drive/folders/1Tgrn2QYyWf0v0eyY1bSGCLlhiqhtIA2n",
  "nodo": "srv-ns8",
  "duracion": 180.5,
  "timestamp": "2026-04-14T10:05:00-03:00",
  "siguiente_paso": "download"
}

Dron 4: dasuten_download_drive-dasu-sql4.rb

Campo Valor
Nodo dasu-sql4
Tarea Descargar backup desde Google Drive vía HTTP
Comando Invoke-WebRequest -Uri "https://drive.usercontent.google.com/download?id=..."
Output File /tmp/dron_download_output.json
Output Clave backup_local

Output JSON:

{
  "paso": "download_complete",
  "backup_local": "F:\\BACKUP\\sysdasuten_compressed_ADN.bak",
  "drive_origen": "rmonla-GDrive:drive_bkps-dasu/sysdasuten_compressed_ADN_20260414_100500.bak",
  "nodo": "dasu-sql4",
  "duracion": 150.3,
  "timestamp": "2026-04-14T10:08:00-03:00",
  "siguiente_paso": "restaurar_backup"
}

Dron 5: dasuten_restaurar_dasu-sql4.rb

Campo Valor
Nodo dasu-sql4
Tarea Restaurar backup (solo RESTORE, sin CHECKDB)
Comando RESTORE DATABASE ... WITH REPLACE, MOVE ...
Output File /tmp/dron_restore_output.json
Output Clave estado_integridad=PENDING_CHECKDB

Output JSON:

{
  "paso": "restore_complete",
  "backup_ruta": "F:\\BACKUP\\sysdasuten_compressed_ADN.bak",
  "nodo": "dasu-sql4",
  "duracion_restore": 0.23,
  "estado_integridad": "PENDING_CHECKDB",
  "timestamp": "2026-04-14T10:10:00-03:00",
  "siguiente_paso": "verificar_integridad"
}

Dron 6: dasuten_verificar-integridad_dasu-sql4.rb

Campo Valor
Nodo dasu-sql4
Tarea Verificar integridad con DBCC CHECKDB
Comando DBCC CHECKDB('sysdasuten') WITH NO_INFOMSGS, ALL_ERRORMSGS
Output File /tmp/dron_verify_output.json
Output Clave estado_integridad, pipeline_completo

Output JSON:

{
  "paso": "verify_complete",
  "nodo": "dasu-sql4",
  "duracion_checkdb": 248.56,
  "estado_integridad": "OK",
  "paginas_verificadas": 1186793,
  "timestamp": "2026-04-14T10:14:00-03:00",
  "pipeline_completo": true
}

🎼 Orquestador: orquestador_pipeline.rb

El orquestador es el cerebro del pipeline. No ejecuta tareas directamente, sino que:

  1. Lanza drones secuencialmente vía dron lanzar
  2. Lee outputs JSON de cada dron completado
  3. Inyecta contexto en el siguiente dron (vía ENV o argumentos)
  4. Decide continuar o abortar según exito/fracaso
  5. Registra estado en la Bitácora Web

Código del Orquestador (simplificado)

#!/usr/bin/env ruby
# adn/tools/cli/drones/orquestador_pipeline.rb

ESTADOS = {
  exportar: {
    script: 'dasuten_exportar.rb',
    nodo: 'srvv-fenix',
    nota: '📤 Exportar backup DASUTEN',
    output_file: '/tmp/dron_export_output.json',
    siguiente: :upload
  },
  upload: {
    script: 'dasuten_upload_drive.rb',
    nodo: 'srv-ns8',
    nota: '☁️ Upload a Google Drive',
    output_file: '/tmp/dron_upload_output.json',
    siguiente: :download
  },
  # ... más pasos
}.freeze

def lanzar_dron(script, nodo, nota, timeout)
  cmd = "#{ADN_RUN} dron lanzar --nota #{nota} --nodo #{nodo} --timeout #{timeout} -- ruby #{script}"
  exito = system(cmd)

  if exito
    # Leer output del dron
    output_file = ESTADOS[nodo.to_sym][:output_file]
    resultado = JSON.parse(File.read(output_file)) if File.exist?(output_file)
    return true, resultado
  else
    return false, {}
  end
end

# Ejecutar pipeline
paso_actual = :exportar
contexto = {}

loop do
  break unless ESTADOS[paso_actual]

  config = ESTADOS[paso_actual]
  exito, resultado = lanzar_dron(config[:script], config[:nodo], config[:nota], timeout)

  unless exito
    puts "❌ Pipeline fallido en paso: #{paso_actual}"
    exit 1
  end

  contexto.merge!(resultado)

  if config[:siguiente]
    paso_actual = config[:siguiente]
  else
    puts "✅ Pipeline completado!"
    break
  end
end

🚀 Uso del Pipeline

Ejecutar Pipeline Completo

# Pipeline completo (todos los pasos encadenados)
./adn/tools/run dron lanzar --nota "Pipeline DASUTEN 8b" -- \
  ruby adn/tools/cli/drones/orquestador_pipeline.rb

Ejecutar Drones Individualmente

# Paso 1: Exportar backup
./adn/tools/run dron lanzar --nota "Exportar backup" --nodo srvv-fenix -- \
  ruby adn/tools/cli/drones/dasuten_exportar.rb

# Paso 2: Upload a Drive
./adn/tools/run dron lanzar --nota "Upload Drive" --nodo srv-ns8 -- \
  ruby adn/tools/cli/drones/dasuten_upload_drive.rb

# Paso 3: Download desde Drive
./adn/tools/run dron lanzar --nota "Download Drive" --nodo dasu-sql4 -- \
  ruby adn/tools/cli/drones/dasuten_download_drive.rb

# Paso 4: Restaurar backup
./adn/tools/run dron lanzar --nota "Restaurar backup" --nodo dasu-sql4 -- \
  ruby adn/tools/cli/drones/dasuten_restaurar.rb

Ejecutar desde un Paso Específico

# Comenzar desde upload (saltear export)
./adn/tools/run dron lanzar --nota "Pipeline DASUTEN" -- \
  ruby adn/tools/cli/drones/orquestador_pipeline.rb --paso upload

Dry-Run (Simulación)

# Ver qué haría sin ejecutar
./adn/tools/run dron lanzar --nota "Pipeline DASUTEN" -- \
  ruby adn/tools/cli/drones/orquestador_pipeline.rb --dry-run

📊 Ventajas de esta Arquitectura

Ventaja Descripción
🧩 Atomicidad Cada dron hace UNA cosa. Fácil de testear, debuggear, reemplazar.
🔗 Intercomunicación JSON como contrato entre drones. Lenguaje-agnóstico.
🔄 Re-ejecución Si falla el paso 3, re-ejecutar solo paso 3 (no todo el pipeline).
👁️ Observabilidad Cada dron se registra individualmente en Bitácora Web.
🛡️ Tolerancia a Fallos Si un dron falla, el pipeline se detiene (fail-fast).
📈 Escalabilidad Agregar nuevos pasos = agregar nuevo dron + registrar en ESTADOS.

📁 Estructura de Archivos

adn/tools/cli/drones/
├── orquestador_pipeline.rb      # Orquestador principal
├── dasuten_exportar.rb          # Dron 1: Exportar backup
├── dasuten_upload_drive.rb      # Dron 2: Upload a Drive
├── dasuten_download_drive.rb    # Dron 3: Download desde Drive
└── dasuten_restaurar.rb         # Dron 4: Restaurar backup

/tmp/
├── dron_export_output.json      # Output del dron 1
├── dron_upload_output.json      # Output del dron 2
├── dron_download_output.json    # Output del dron 3
└── dron_restore_output.json     # Output del dron 4

🔮 Futuras Mejoras (Fases Pendientes)

Fase 2: Persistencia en Bitácora

Actualmente los outputs JSON son efímeros (/tmp/). La Fase 2 persistirá los outputs en la Bitácora Web:

# En dron_db.rb
CREATE TABLE drone_outputs (
  id SERIAL PRIMARY KEY,
  dron_id VARCHAR(50) REFERENCES drones(dron_id),
  output JSONB NOT NULL,
  created_at TIMESTAMP DEFAULT NOW()
);

-- El orquestador puede leer outputs de la DB en lugar de archivos
output = DronDB.obtener_output(dron_id)

Fase 3: Reintentos Automáticos

# Orquestador con retry
def lanzar_dron_con_retry(script, nodo, nota, timeout, max_retries: 3)
  max_retries.times do |intento|
    exito, resultado = lanzar_dron(script, nodo, nota, timeout)
    return true, resultado if exito

    puts "⚠ Dron falló (intento #{intento + 1}/#{max_retries})"
    sleep(2 ** intento)  # Backoff exponencial
  end
  return false, {}
end

Fase 4: Condicionales de Skip

# Skip si ya está hecho
def deberia_ejecutar?(paso, contexto)
  case paso
  when :upload
    # Skip si ya hay backup reciente en Drive
    !backup_reciente_en_drive?(contexto[:backup_ruta])
  when :restaurar
    # Skip si BD ya está actualizada
    !bd_actualizada?(contexto[:backup_local])
  else
    true
  end
end

📝 Lecciones Aprendidas

Lo que Funcionó

Lección Descripción
JSON como contrato Simple, legible, lenguaje-agnóstico. Funciona mejor que DB compartida.
Outputs en /tmp Fácil de limpiar, no requiere permisos especiales.
Orquestador stateless El estado está en los JSONs, no en el orquestador.
Drones auto-bitácora Cada dron se registra individualmente en la Bitácora Web.
Google Drive como intermediario Elimina dependencia de Tailscale inestable. Download HTTP directo desde dasu-sql4.
Base64 encoding para PowerShell Evita problemas de escaping UTF-8 al transportar scripts sobre SSH.
sshpass + ProxyCommand Patrón efectivo para acceder a VMs detrás de relay (dasu-sql4 detrás de srv-dasu).

📊 Resultados del Pipeline DASUTEN (2026-04-14)

Métrica Valor
Archivo transferido 1.3 GB (sysdasuten_compressed_ADN.bak)
Tiempo total de pipeline ~35 minutos
Upload a Drive 29 minutos (1736s) — limitado por ancho de banda
Download HTTP ~5 minutos — con confirmación de virus manejada
Restore SQL 0.23 segundos — MOVE instantáneo
DBCC CHECKDB 248.56 segundos — Integridad: OK
Throughput efectivo ~650 KB/s (limitado por subida a Drive)

⚠️ Lo que Requiere Atención

Lección Descripción
Limpieza de archivos Los JSONs en /tmp/ deben limpiarse post-pipeline.
Timeouts por dron Cada dron debe tener timeout individual (no todos duran lo mismo).
Manejo de errores El orquestador debe capturar stderr de cada dron para debugging.

🔧 Desafíos Superados

Desafío Solución
Virus confirmation page Parseo de HTML con regex PowerShell para extraer id, uuid, confirm
dasu-sql4 sleep mode Acceso vía relay SSH desde srv-dasu con sshpass -e + ProxyCommand
PowerShell escaping Base64 encoding del script completo + heredoc con 'PSCMD' (sin interpolación)
sqlcmd no disponible en PATH Usar ruta completa: C:\Program Files\...\sqlcmd.exe

🔗 Referencias


📊 Estado del Pipeline DASUTEN (6 drones atómicos)

Paso Estado Dron Nodo Output Duración
8b.1 Completado dasuten_exportar_srvv-fenix.rb srvv-fenix backup_ruta 44.8s
8b.2 Completado dasuten_transferir_srvv-fenix-srv-ns8.rb srv-ns8 backup_local ~2 min
8b.3 Completado dasuten_upload_srv-ns8-drive.rb srv-ns8 drive_ruta 1736s (29 min)
8b.4 Completado dasuten_download_drive-dasu-sql4.rb dasu-sql4 backup_local ~5 min
8b.5 Completado dasuten_restaurar_dasu-sql4.rb dasu-sql4 estado_integridad=PENDING 0.23s
8b.6 Completado dasuten_verificar-integridad_dasu-sql4.rb dasu-sql4 estado_integridad=OK 248.56s

Pipeline completado exitosamente el 2026-04-14 a las 17:21.

Resultados Finales

  • Backup comprimido: 1.3 GB (sysdasuten_compressed_ADN.bak)
  • Google Drive URL: rmonla-GDrive:drive_bkps-dasu/sysdasuten_compressed_ADN_20260414_*.bak
  • Download HTTP directo: Con confirmación de virus manejada (formulario HTML parsing)
  • Restore: 0.23 segundos (MOVE a F:\DATA y F:\LOG)
  • DBCC CHECKDB: 248.56 segundos — Integridad: OK (1,186,793 páginas verificadas, sin errores)
  • Duración total del pipeline: ~35 minutos

🔄 Pipeline Diferencial (Refresco Diario)

Para refrescos diarios de la base de datos, se recomienda usar backup diferencial que solo transfiere los cambios desde el último backup completo.

Ventajas del Pipeline Diferencial

Métrica Completo Diferencial
Tamaño backup 1.3 GB ~50-100 MB*
Tiempo export 45 seg ~5-10 seg
Tiempo upload 29 min ~2-3 min
Tiempo download ~5 min ~30 seg
Tiempo total ~35 min ~5-10 min

* Depende de la cantidad de cambios diarios

Drones del Pipeline Diferencial

Paso Dron Nodo Tarea
1 dasuten_exportar_diferencial_srvv-fenix.rb srvv-fenix BACKUP DATABASE ... WITH DIFFERENTIAL
2 dasuten_transferir_srvv-fenix-srv-ns8.rb srv-ns8 SMB → /var/tmp/
3 dasuten_upload_srv-ns8-drive.rb srv-ns8 rclone → Google Drive
4 dasuten_download_drive-dasu-sql4.rb dasu-sql4 HTTP ← Google Drive
5 dasuten_restaurar_diferencial_dasu-sql4.rb dasu-sql4 RESTORE ... WITH DIFFERENTIAL + RECOVERY
6 dasuten_verificar-integridad_dasu-sql4.rb dasu-sql4 DBCC CHECKDB

Orquestador Diferencial

# Ejecutar pipeline diferencial completo
./adn/tools/run dron lanzar --nota "Refresco Diario DASUTEN" -- \
  ruby adn/tools/cli/drones/orquestador_pipeline_diferencial.rb

Estrategia Recomendada

Día Tipo de Backup Duración Estimada
Lunes Completo ~35 min
Martes Diferencial ~5-10 min
Miércoles Diferencial ~5-10 min
Jueves Diferencial ~5-10 min
Viernes Diferencial ~5-10 min
Sábado Diferencial ~5-10 min
Domingo — (sin cambios)

Consideraciones Importantes

  1. El backup diferencial se basa en el último backup completo, no en el diferencial anterior
  2. Cada diferencial es acumulativo — el martes tiene cambios desde lunes, el miércoles tiene cambios desde lunes (no desde martes)
  3. Restaurar diferencial requiere:
    • Último backup completo (ya restaurado en dasu-sql4)
    • Backup diferencial más reciente
    • WITH NORECOVERY para aplicar diferencial, luego WITH RECOVERY para poner BD online

Documento creado: 2026-04-14 Última actualización: 2026-04-14 Versión: 2.1 — Con pipeline diferencial