Backup y restore online de Software AG ADABAS vía utilitarios nativos adabck/adaopr/adavfy/adarec, con Level F (full) streaming, Level I (PLOG archiving) con safety gate, PITR por checkpoint, cancel signal-safe, y timeouts wall-clock por subproceso.
Documento técnico complementario a la página del plugin PodHeitor ADABAS.
1. El problema: Bacula stock no respalda ADABAS
ADABAS es una de las pocas bases NoSQL jerárquicas que sobreviven en workloads mainframe-derived (telecom, gobierno, seguros). Bacula stock no tiene plugin ADABAS. Bacula Enterprise no lista plugin ADABAS en ningún manual público (verificado en BE 6.x → 18.x). Las alternativas:
- Scripts shell wrappers alrededor de
adabck DUMP=*— comunes en campo pero frágiles: sin RAII deEXT_BACKUP=PREPARE/CONTINUE(dejan el nucleus en estado inconsistente frozen si el script muere), sin detección de wrap en PLOG, sin cleanup signal-safe, sin manifest cataloging. - Agentes ADABAS de NetBackup / Commvault — caros y fuera del mundo Bacula.
PodHeitor ADABAS llena ese hueco con integración first-class a los utilitarios nativos. Propuesta de valor one-line: «configure un Bacula job. ADABAS se restaura a un punto en el tiempo consistente con un solo bconsole restore.»
2. Modelo arquitectónico
Bacula Director ─── Plugin = "podheitor-adabas: dbid=12"
│
▼
Bacula File Daemon (bacula-fd)
└── /opt/bacula/plugins/podheitor-adabas-fd.so (cdylib Rust)
│ PTCOMM (length-prefixed packets via stdin/stdout)
▼
/opt/bacula/bin/podheitor-adabas-backend (binario Rust)
├── main.rs PTCOMM 5-phase handshake
├── config.rs merge plugin string + config-file
├── backup.rs orquestación Level F
├── incremental.rs orquestación Level I (PLOG archiving)
├── restore.rs restore Phase A→E
├── adabck.rs wrappers de subproceso DUMP + RESTORE
├── adaopr.rs Operator: status + EXT_BACKUP + RAII guard
├── adavfy.rs verify de consistencia post-restore
├── adarec.rs PLOG apply (license-gated en CE)
├── plog.rs discovery de PLOG + detección de wrap
├── state.rs state file (archived + pending_delete)
├── stream.rs buffer fijo 1 MiB child_stdout → PTCOMM
└── subproc.rs helper bounded-wait (timeouts)
ADABAS host (misma máquina que bacula-fd)
adabck / adaopr / adavfy / adarec
DBID=<N> ASSO / DATA / WORK containers
Invariantes críticas:
- El backend Rust corre en la misma máquina Linux que ADABAS. ADABAS IPC es SysV message queue — no atraviesa máquinas.
- El Storage Daemon de Bacula puede estar en cualquier lugar; el plugin solo streama PTCOMM packets.
- Cada subproceso tiene wall-clock timeout (plan §19). Un utilitario runaway no puede bloquear el job Bacula.
3. Capacidades
| Capacidad | Estado |
|---|---|
Full (Level F) backup online vía adabck DUMP=* streamed a Bacula |
✅ CE-validado E2E |
| Incremental (Level I) PLOG archiving con detección de wrap + safety-gated delete | ✅ orchestration CE-validada; emisión real de PLOG requiere ADABAS licenciado |
| Multi-DBID jobs con reporting agregado best-effort | ✅ CE-validado |
Full restore vía adabck RESTORE=* con staged-file + reconstrucción de BackupManifest |
✅ orchestration CE-validada; restore real requiere nucleus-offline (license-gated) |
PITR vía adarec CHECKPOINT=(first,last) |
✅ code paths + unit tests; replay live license-gated |
Stream mode: stdout (BCK001=-, default) y tempfile fallback |
✅ ambos validados |
| Timeouts configurables (status / backup / restore / adarec / verify) | ✅ |
Verbosidad de log vía env ADABAS_LOG_LEVEL |
✅ |
| Cancel signal-safe (SIGTERM/SIGINT → unwind de staged files) | ✅ |
4. EXT_BACKUP RAII guard — invariante crítica
El flujo de «consistencia transaccional» de ADABAS es: adaopr EXT_BACKUP=PREPARE, luego adabck DUMP=*, luego adaopr EXT_BACKUP=CONTINUE. Si el middle step crashea y CONTINUE no se ejecuta, el nucleus queda en estado frozen esperando — producción muere.
adaopr.rs empareja PREPARE/CONTINUE vía RAII de Rust: la struct ExtBackupGuard llama PREPARE en construcción y CONTINUE en Drop. Incluso un panic o unwinding signal-induced garantiza el CONTINUE — el nucleus nunca queda frozen.
5. PLOG archiving y safety-gated delete
Jobs Level I (Incremental) descubren PLOGs rotados desde el job anterior, archivan cada uno, y marcan para borrado. El borrado solo ocurre en un job subsecuente, tras comprobado que el anterior fue catalogado con éxito. Esto da al operador un ciclo completo de job para abortar antes de perder copias locales.
Detección de wrap en PLOG: si la secuencia sequencial salta (porque ADABAS rotó y descartó un PLOG sin que lo archiváramos), el plugin aborta con mensaje claro — sin intentar continuar y producir una chain inconsistente.
6. Parámetros del plugin string
| Parámetro | Default | Descripción |
|---|---|---|
dbid |
(required) | ID único de DB ADABAS |
dbids |
— | Lista comma-separated para multi-DB jobs: dbids=12,13,14 |
external_backup |
yes |
Wrap DUMP en adaopr EXT_BACKUP=PREPARE/CONTINUE. Debe ser no en CE |
plog |
yes |
Archiva PLOGs en jobs Level I. No-op en CE |
stream_mode |
auto |
auto (= stdout), stdout, o tempfile fallback |
buffer_size |
8m |
Buffer de streaming (acepta sufijos K/M/G) |
allow_destructive_restore |
no |
Gate obligatorio para restore. Sin esto, restore se rehúsa a sobrescribir DB existente |
verify_after_restore |
yes |
Corre adavfy DBID=<N> después del restore |
restore_to_checkpoint |
— | Target PITR: nombres de checkpoint "first,last" |
backup_timeout_secs |
14400 | Wall-clock bound en DUMP (4h) |
restore_timeout_secs |
14400 | Wall-clock bound en RESTORE (4h) |
adarec_timeout_secs |
1800 | Wall-clock per-PLOG en adarec (30 min) |
plog_ship_retries |
2 | Max retries (exponential backoff) en transient PLOG ship failure. Full backup nunca es retried — ADR-009 |
7. Restore destructivo con gate
Restore sobrescribe DBID existente — operación destructiva. El plugin se rehúsa a ejecutarla a menos que el operador pase explícitamente:
pluginoptions "podheitor-adabas: dbid=12 allow_destructive_restore=yes"
Sin ese flag, el job aborta con mensaje claro. PITR lleva el mismo gate:
pluginoptions "podheitor-adabas: dbid=12 allow_destructive_restore=yes restore_to_checkpoint=SYNC,W8NW"
8. Limitaciones de Community Edition
ADABAS CE es gratuito pero tiene restricciones comportamentales que afectan la semántica de backup. El path de código del plugin es correcto para ADABAS licenciado; CE simplemente falla en porciones:
| Restricción | Impacto |
|---|---|
Sin soporte a PLOG (PLOG=<reset> en startup) |
Jobs Level I encuentran 0 PLOGs candidatos. Orchestration testeada vía fixtures sintéticos |
EXT_BACKUP=PREPARE + adabck DUMP=* deadlock |
En CE, PREPARE antes de DUMP causa do_msgrcv esperando IPC. Use external_backup=no en CE |
adabck RESTORE=* requiere nucleus offline |
Single nucleus de CE no puede ser cleanly stopped/restarted desde el entrypoint del container |
adarec (PLOG apply) requiere PLOGs |
Consecuentemente license-gated |
9. Anti-patterns documentados
- No use
:como separador de parámetros. Los parámetros son space-separated;:solo aparece traspodheitor-adabas. Pegadinha común. - No corra
external_backup=yesen CE. Deadlock garantizado. - No edite el state file a mano (
/opt/bacula/working/podheitor-adabas-state/dbid-<N>.json). El plugin usa write-then-rename para atomicidad; edits manuales corrompen el tracking de archived/pending_delete. - No deshabilite
panic = "abort"en release profile. Panic atravesando FFI víapluginIOes UB.
10. Postura de licencia
El plugin se distribuye bajo AGPLv3 — misma licencia que Bacula Community Edition. Compatible con uso open-source sin costo, y la base para deploys comerciales bajo acuerdos proprietary single-license para clientes que necesitan aislamiento legal.
¿Listo para evaluar?
Trial gratuito de 30 días para instancias ADABAS calificadas (CE 7.x o licensed 8.x+). Garantizamos al menos 50% de descuento vs Bacula Enterprise (que ni siquiera tiene plugin ADABAS), Veeam, o Commvault, con más funcionalidades — incluyendo PLOG safety gate y RAII de EXT_BACKUP que los scripts shell jamás entregan.
Heitor Faria — Fundador, PodHeitor International
✉ [email protected]
☎ +1 (789) 726-1749 · +55 (61) 98268-4220 (WhatsApp)
🔗 Página del plugin PodHeitor ADABAS
Disponível em:
Português (Portugués, Brasil)
English (Inglés)
Español