Online backup and restore of Software AG ADABAS via the native adabck/adaopr/adavfy/adarec utilities, with Level F (full) streaming, Level I (PLOG archiving) with safety gate, checkpoint-based PITR, signal-safe cancel, and per-subprocess wall-clock timeouts.
Companion document to the PodHeitor ADABAS plugin page.
1. The problem: stock Bacula can’t back up ADABAS
ADABAS is one of the rare hierarchical NoSQL databases that survives in mainframe-derived workloads (telecom, government, insurance). Stock Bacula has no ADABAS plugin. Bacula Enterprise lists no ADABAS plugin in any public manual (verified across BE 6.x → 18.x). The alternatives:
- Shell wrapper scripts around
adabck DUMP=*— common in the field but fragile: no RAII forEXT_BACKUP=PREPARE/CONTINUE(leaving the nucleus in an inconsistent frozen state if the script dies), no PLOG wrap detection, no signal-safe cleanup, no manifest cataloging. - NetBackup / Commvault ADABAS agents — expensive and outside the Bacula world.
PodHeitor ADABAS fills that gap with first-class integration to native utilities. One-line value proposition: “Configure a Bacula job. ADABAS is restored to a consistent point-in-time with a single bconsole restore.”
2. Architectural model
Bacula Director ─── Plugin = "podheitor-adabas: dbid=12"
│
▼
Bacula File Daemon (bacula-fd)
└── /opt/bacula/plugins/podheitor-adabas-fd.so (Rust cdylib)
│ PTCOMM (length-prefixed packets via stdin/stdout)
▼
/opt/bacula/bin/podheitor-adabas-backend (Rust binary)
├── main.rs PTCOMM 5-phase handshake
├── config.rs plugin string + config-file merge
├── backup.rs Level F orchestration
├── incremental.rs Level I (PLOG archiving) orchestration
├── restore.rs Phase A→E restore orchestration
├── adabck.rs DUMP + RESTORE subprocess wrappers
├── adaopr.rs Operator: status + EXT_BACKUP + RAII guard
├── adavfy.rs Post-restore consistency verify
├── adarec.rs PLOG apply (license-gated on CE)
├── plog.rs PLOG discovery + sequence-wrap detection
├── state.rs Plugin state file (archived + pending_delete)
├── stream.rs Fixed 1 MiB buffer child_stdout → PTCOMM
└── subproc.rs Bounded-wait helper (timeouts)
ADABAS host (same machine as bacula-fd)
adabck / adaopr / adavfy / adarec
DBID=<N> ASSO / DATA / WORK containers
Critical invariants:
- The Rust backend runs on the same Linux host as ADABAS. ADABAS IPC is SysV message queue — cannot be tunnelled across machines.
- Bacula’s Storage Daemon can be anywhere; the plugin just streams PTCOMM packets.
- Every subprocess has a bounded wall-clock timeout (plan §19). A runaway utility cannot wedge the Bacula job.
3. Capabilities
| Capability | Status |
|---|---|
Full (Level F) online backup via adabck DUMP=* streamed to Bacula |
✅ CE-validated end-to-end |
| Incremental (Level I) PLOG archiving with sequence-wrap detection + safety-gated delete | ✅ orchestration CE-validated; real PLOG emission needs licensed ADABAS |
| Multi-DBID jobs with best-effort aggregate reporting | ✅ CE-validated |
Full restore via adabck RESTORE=* with staged-file + BackupManifest reconstruction |
✅ orchestration CE-validated; actual DB restore requires nucleus-offline (license-gated) |
Point-in-time restore via adarec CHECKPOINT=(first,last) |
✅ code paths + unit tests; live replay license-gated |
Stream mode: stdout (BCK001=-, default) and tempfile fallback |
✅ both validated |
| Configurable timeouts (status / backup / restore / adarec / verify) | ✅ |
Log verbosity via ADABAS_LOG_LEVEL env |
✅ |
| Signal-safe cancel (SIGTERM/SIGINT → unwind staged files) | ✅ |
4. EXT_BACKUP RAII guard — critical invariant
ADABAS’s “transactional consistency” flow is: adaopr EXT_BACKUP=PREPARE, then adabck DUMP=*, then adaopr EXT_BACKUP=CONTINUE. If the middle step crashes and CONTINUE never runs, the nucleus stays in a frozen waiting state — production goes down.
adaopr.rs pairs PREPARE/CONTINUE via Rust’s RAII: the ExtBackupGuard struct calls PREPARE in its constructor and CONTINUE in Drop. Even a panic or signal-induced unwinding guarantees CONTINUE — the nucleus is never left frozen.
5. PLOG archiving and safety-gated delete
Level I (Incremental) jobs discover PLOGs rotated since the previous job, archive each, and mark for deletion. The deletion only happens in a subsequent job, after the previous one is proven cataloged successfully. This gives the operator one full job cycle to abort before losing local copies.
PLOG wrap detection: if the sequential sequence skips (because ADABAS rotated and discarded a PLOG before we archived it), the plugin aborts with a clear message — without trying to continue and producing an inconsistent chain.
6. Plugin string parameters
| Parameter | Default | Description |
|---|---|---|
dbid |
(required) | Single ADABAS database ID |
dbids |
— | Comma-separated list for multi-DB jobs: dbids=12,13,14 |
external_backup |
yes |
Wrap DUMP in adaopr EXT_BACKUP=PREPARE/CONTINUE for transactional consistency. Must be no on CE |
plog |
yes |
Archive PLOGs during Level I jobs. No-op on CE |
stream_mode |
auto |
auto (= stdout), stdout, or tempfile fallback |
buffer_size |
8m |
Streaming buffer (accepts K/M/G suffixes) |
allow_destructive_restore |
no |
Required gate for restore. Without this, restore refuses to overwrite an existing DB |
verify_after_restore |
yes |
Run adavfy DBID=<N> after restore |
restore_to_checkpoint |
— | PITR target: "first,last" checkpoint names |
backup_timeout_secs |
14400 | Wall-clock bound on DUMP (4h) |
restore_timeout_secs |
14400 | Wall-clock bound on RESTORE (4h) |
adarec_timeout_secs |
1800 | Per-PLOG wall-clock on adarec (30 min) |
plog_ship_retries |
2 | Max retries (exponential backoff) on transient PLOG ship failure. Full backup is never retried — ADR-009 |
7. Destructive restore with gate
Restore overwrites the existing DBID — destructive operation. The plugin refuses unless the operator explicitly passes:
pluginoptions "podheitor-adabas: dbid=12 allow_destructive_restore=yes"
Without that flag, the job aborts with a clear message. PITR carries the same gate:
pluginoptions "podheitor-adabas: dbid=12 allow_destructive_restore=yes restore_to_checkpoint=SYNC,W8NW"
8. Community Edition limitations
ADABAS Community Edition is free but has behavioural restrictions that affect backup semantics. The plugin code path is correct for licensed ADABAS; CE simply fails to drive portions of it:
| Restriction | Impact |
|---|---|
No PLOG support (PLOG=<reset> at startup) |
Level I jobs find 0 candidate PLOGs. Orchestration tested via synthetic fixtures |
EXT_BACKUP=PREPARE + adabck DUMP=* deadlock |
On CE, PREPARE before DUMP causes do_msgrcv waiting for IPC. Use external_backup=no on CE |
adabck RESTORE=* requires nucleus offline |
CE’s single nucleus can’t be cleanly stopped/restarted from a container entrypoint |
adarec (PLOG apply) requires PLOGs |
Consequently license-gated |
9. Documented anti-patterns
- Don’t use
:as a parameter separator. Parameters are space-separated;:only followspodheitor-adabas. Common pitfall. - Don’t run
external_backup=yeson CE. Guaranteed deadlock. - Don’t hand-edit the state file (
/opt/bacula/working/podheitor-adabas-state/dbid-<N>.json). The plugin uses write-then-rename for atomicity; manual edits corrupt archived/pending_delete tracking. - Don’t disable
panic = "abort"in release profile. A panic crossing FFI viapluginIOis UB.
10. License posture
The plugin ships under AGPLv3 — same license as Bacula Community Edition. Open-source compatible at no cost, and the basis for commercial deploys under proprietary single-license agreements for customers requiring legal isolation.
Ready to evaluate?
30-day free trial for qualified ADABAS instances (CE 7.x or licensed 8.x+). We guarantee at least 50% off vs Bacula Enterprise (which has no ADABAS plugin), Veeam, or Commvault, with more features — including PLOG safety gate and EXT_BACKUP RAII that shell scripts will never deliver.
Heitor Faria — Founder, PodHeitor International
✉ [email protected]
☎ +1 (789) 726-1749 · +55 (61) 98268-4220 (WhatsApp)
🔗 PodHeitor ADABAS plugin page
Disponível em:
Português (Portuguese (Brazil))
English
Español (Spanish)