Engine de deduplicação global standalone que opera como sidecar do Storage Daemon, com chunking de tamanho variável (FastCDC), Bloom filter multicamadas, RocksDB index, deduplicação client-side via FD plugin, e métricas Prometheus — competindo diretamente com Bacula Enterprise GED e Commvault DDB.
Documento técnico complementar à página do plugin PodHeitor Global Dedup.
1. O problema: GED do Bacula Enterprise é fixed-block + TokyoCabinet
O GED (Global Endpoint Deduplication) do Bacula Enterprise usa chunking de tamanho fixo e TokyoCabinet B-tree como hash DB. Isso tem três limitações estruturais:
- Cascade effect em modificações. Inserir 1 byte no início de um arquivo de 10 GB invalida todos os blocos subsequentes — o boundary fixed-block desalinha. Resultado: ratio de dedup colapsa em workloads de log, dump de DB, e snapshots de VM.
- SSD bound em IOPS. Cada lookup TokyoCabinet é um seek SSD. Sem Bloom filter na frente, o caminho hot toca disco a cada bloco.
- Sem client-side dedup. Todo byte cruza o link FD→SD antes de a dedup acontecer. Em backup remoto, isso desperdiça banda.
O PodHeitor GDD ataca os três: FastCDC variable-length, Bloom filter L1 + RocksDB L2, e fingerprint client-side via FD plugin antes de transmitir o payload.
2. Arquitetura — sidecar SD + FD plugin client-side
┌─────────────────────────────────────────────────────────────────────────────┐
│ BACULA INFRASTRUCTURE (UNMODIFIED) │
├─────────────────────────────────────────────────────────────────────────────┤
│ ┌──────────┐ ┌──────────────┐ ┌──────────────────────────┐ │
│ │ Bacula │ TCP │ Bacula FD │ TCP │ Bacula SD │ │
│ │ Director │◄──────►│ + PodHeitor │◄──────►│ + PodHeitor SD Plugin │ │
│ │ │ │ FD Plugin │ │ (Rust cdylib v2) │ │
│ └──────────┘ └──────┬───────┘ └───────────┬──────────────┘ │
│ │ Fingerprints │ Data stream │
│ ▼ ▼ │
├─────────────────────────────────────────────────────────────────────────────┤
│ PODHEITOR GDD ENGINE (RUST) │
├─────────────────────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌───────────────┐ ┌──────────────────────────┐ │
│ │ FastCDC │ │ Bloom Filter │ │ Dedup Index │ │
│ │ Chunker │──►│ (L1 Cache) │──►│ (RocksDB + RAM) │ │
│ │ (variable) │ │ FPR: 0.1% │ │ SHA-256 → ContainerRef │ │
│ └─────────────┘ └───────────────┘ └──────────────────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌─────────────┐ ┌──────────────────────────┐ │
│ │ Segment │ │ Container Store │ │
│ │ Locality │────────────────────►│ (append-only files) │ │
│ │ Tracker │ │ + segment grouping │ │
│ └─────────────┘ └──────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
O FD plugin é cdylib Rust (workspace plugin-gdd). O SD plugin é cdylib Rust + binding FFI (libgdd_ffi_sd.so) que conversa com o daemon GDD via TCP+PSK na porta 9104, com métricas Prometheus na 9421. Zero modificação ao source do Bacula.
3. FastCDC — chunking de tamanho variável
FastCDC (Fast Content-Defined Chunking) usa rolling hash baseado em gear table para encontrar boundaries determinísticas baseadas em conteúdo:
| Parâmetro | Default | Range |
|---|---|---|
| Min chunk | 4 KB | 2–8 KB |
| Avg chunk | 16 KB | 8–64 KB |
| Max chunk | 64 KB | 32–256 KB |
| Hash bits | 48 | fixo |
Por que variable-length em vez de fixed-size? Em modificações que inserem ou removem bytes, fixed-size desalinha tudo após o ponto de inserção (cascade effect). Variable-length re-encontra o boundary natural após poucos bytes — 50–70% melhor ratio em DB dumps, logs, snapshots de VM.
4. Bloom filter L1 — eliminação de I/O SSD
Antes de qualquer query ao RocksDB (custosa em SSD), o Bloom filter em RAM responde “definitivamente novo” ou “talvez existe”. Counting Bloom Filter com partitioned hashing:
| Parâmetro | Valor | Racional |
|---|---|---|
| Items esperados | 100M chunks | ~1.6 TB unique data em 16 KB avg |
| FPR target | 0.1% (1 em 1000) | Aceitável |
| Bits per item | 14.4 | Optimal para 0.1% FPR |
| Memória total | ~180 MB | Cabe em RAM facilmente |
| Hash functions | 10 | k = ln(2) × m/n |
| Persistência | mmap’d file | Load instantâneo, crash-safe |
Resultado mensurado: 90%+ de redução de I/O SSD no caminho hot vs lookup direto em DB.
5. Dois modos de operação
| Mode | Comportamento | Quando usar |
|---|---|---|
| Mode A | SD plugin transparente; dedup acontece server-side; FD não muda | Migration path / clientes legados que não podem mudar FD |
| Mode B | FD plugin computa fingerprint client-side; só blocos novos atravessam o link | Default produção — economia de 60–95% de banda |
Em Mode B, o FD lê o arquivo, FastCDC chunk-a, gera SHA-256 fingerprint por chunk, envia a lista ao GDD daemon. O daemon responde “já tenho” ou “preciso”. O FD só transmite o que é novo.
6. Reliability — fail-closed obrigatório
O lab Mode B passou por sequência de bugs documentados (B3a/b/c/d, B4, B5) que ensinaram um princípio operacional: fail-closed > fail-open. Em particular, qualquer error path que deixe state->active=false com sp->no_read=false emite silenciosamente file records de 0 bytes com Termination: Backup OK — a pior falha possível em backup. As correções de produção 2026-04-29 → 2026-05-01:
| Bug | Causa | Fix |
|---|---|---|
| B3c (daemon-restart mid-job) | state stale após reconnect | Fail-closed JOB_START + per-file passthrough fallback (com lseek(fd, 0, SEEK_SET) antes de reusar fd) |
| B3a (FD kill) | Job ficava em status I infinito | Auto-reschedule pelo Director |
| B3b (SD kill) | Volume parcial sem detecção | Fatal status; recovery via re-run |
| B5 (cargo 0-byte) | VREF build 24.6 MB excedia STORE_NEW cap 16 MiB | FDRAW passthrough em três paths de fallback |
| Foreign-plugin guard | Plugin engatava em jobs ADABAS | SELF_PREFIX gate em PluginCommand |
7. Garbage collection — concurrent mark-sweep
O GDD daemon roda vacuum concorrente em background: mark-sweep sobre o index, refcount drop em containers, compaction de container holes. Ao contrário do GED do BEE (manutenção em “DDB maintenance job” agendado), o vacuum do PodHeitor:
- Roda com priority baixa, sem janela.
- Não bloqueia jobs ativos (read+write em containers append-only).
- Reporta progresso via
/metricsPrometheus.
8. BR/DR — chunk-store recovery
O BR_DR_RUNBOOK.md documenta cold-stop tar + LVM/ZFS snapshot + recovery soft-loss (delete bloom/esm, daemon rebuilds). Caveats:
gdd-fsck --rebuild-indexainda não implementado (post-GA item).manifests/empty no estado atual — write path em verificação.- Live-snapshot API não disponível; usar LVM/ZFS no host.
9. Métricas Prometheus
Endpoint /metrics na porta 9421 expõe:
gdd_chunks_total,gdd_chunks_unique,gdd_chunks_duplicategdd_bytes_in,gdd_bytes_stored, ratio derivadogdd_bloom_hits,gdd_bloom_false_positivesgdd_rocksdb_get_seconds_bucket(histogram)gdd_vacuum_cycles_total,gdd_vacuum_bytes_reclaimedgdd_daemon_rss_bytes(target ≤ 200 MB sustentado)
10. Anti-patterns documentados
- Não rode dedup em arquivo único > 134 MiB cap sem FDRAW passthrough. O VREF protocol cap é 16 MiB; arquivos grandes vão por OVERSIZE-PASSTHROUGH. O lab provou isso com
randombig.binde 12 GB cruzando rollover de volume. - Não confunda volume size mismatch com bug do plugin. Diferenças
abs(N-M) < 65536são self-heal do core do Bacula — runbook B1 do operator. - Não rode operações concorrentes sobre o mesmo container sem o daemon ativo. O daemon é a única fonte de verdade do refcount.
- Não derrube o daemon mid-job sem aceitar fail-closed do job atual. Comportamento intencional: fail-closed por design vs silent 0-byte bug.
11. License posture
O plugin distribui sob LicenseRef-PodHeitor-Proprietary. Não vincula estaticamente nenhum source AGPLv3 do Bacula. O FD cdylib + SD cdylib + daemon são todos Rust binaries puros. A integração SD usa apenas a sd_plugins.h API pública do Bacula via extern "C".
Pronto para avaliar?
Trial gratuito de 30 dias para workloads multi-TB com dedup ratio esperado > 4×. Garantimos no mínimo 50% de desconto vs Bacula Enterprise GED, Veeam Repository ou Commvault DDB, com mais funcionalidades inclusas.
Heitor Faria — Fundador, PodHeitor International
✉ [email protected]
☎ +1 (789) 726-1749 · +55 (61) 98268-4220 (WhatsApp)
🔗 Página do plugin PodHeitor Global Dedup
Disponível em:
Português
English (Inglês)
Español (Espanhol)