Replies: 2 comments
|
@drew-512 thanks for the thorough writeup — the durability callout is a good one, and you're right about the part that matters. One correction on the detail: The gap you're pointing at is real, though. The WAL and value log are memory-mapped and synced via The fix would be a platform-gated |
|
Hello from Austin Texas. Right on, appreciate your time and consideration mate. Will let it simmer for a bit and will post back here with a PR sketch for feedback. Appreciate all y'alls work on badger. |
Uh oh!
There was an error while loading. Please reload this page.
Aloha badger friends,
We just completed a several-day, best-tuned-vs-best-tuned evaluation of Badger
v4.9.2 against Pebble (v1.1.5, then v2.1.6) as the storage engine for a
federated content platform — an append-heavy durable journal plus an
LSM-projection store, behind a seam that runs both engines against identical
workloads. Sharing the Badger-relevant findings in case they're useful
upstream.
Method: seed-deterministic synthetic data (a small-value-heavy
"representative" profile and a stress profile with 12% 1 MiB values), 6-run
interleaved medians, Go 1.26.3, on three hosts: a quiet 6-core arm64 Linux VM
(ext4), a 1-CPU x86_64 Linux box under live service load (ext4), and an M1 Pro
(macOS 14.8.7).
Where Badger clearly won
The value-log architecture is the story: with Pebble v1 (no value
separation), Badger won the 1 MiB-value axis by 2.7–6× everywhere. Pebble v2's
blob-file value separation closed most of that, but Badger still owns bulk
ingest of large values and the extreme tail on constrained hardware.
Things that bit us (v4.9.2 specifics)
With default options,
MaxBatchCount≈ 104K entries — a single 3.5 MBapplication message expanding to ~95K entries hits it, and
ErrTxnTooBigfrom a mid-batch commit poisons the batch. We now pre-check every unit
against 0.9×
DB.MaxBatchSize()/MaxBatchCount()and classify beforecommitting. Suggestion: the docs could call out how low the entry-count
ceiling sits relative to real-world message sizes.
seeking to a manually 0xFF-padded successor; we shipped a silent bug from
getting this wrong, and per-step reverse iteration (skiplist descent +
tombstone wading) benchmarked ~2.7× slower than an equivalent forward scan
at 1M keys, so we restructured to forward walks. A native bounded/reverse
helper would remove a real footgun.
NumMemtables5→8 andNumLevelZeroTablesStallto 30 (compression off) improved mediansslightly but worsened durable-append p99.9 at 100K-tx scale (6.3 ms vs
2.9 ms stock) — more buffered state, burstier flush/compaction debt.
Stock settings were the better tail choice for us.
Item.Valuebytes pastNext()into an async writer — harmless on Badger,wire corruption on engines that recycle buffers. Not a Badger defect, but
its leniency hid the bug for months.
Questions
On macOS, is
SyncWrites=trueintended to guarantee power-loss durability?Reading v4.9.2 (with ristretto's
z/mmap_darwin.go), the WAL/vlog/table syncpaths go through
msync(MS_SYNC)/File.Syncon mmap'd files, and we don'tsee an
F_FULLFSYNC(fcntl) anywhere — on Darwin, plain fsync/msync does notflush the drive cache, so acknowledged writes can be lost on power failure.
(This is also why Badger benches ~50–80× faster than F_FULLFSYNC-honest engines
on Macs — an unfair comparison in both directions.) If this is by design,
a doc note would help; if not, would a platform-gated full-fsync option be
welcome as a PR?
I've been a fan of badger for years now, but unfortunately
we ultimately chose Pebble v2 for value separation and unbounded batch
semantics fitting our shape — but Badger's large-value ingest and
constrained-hardware tails remain genuinely best-in-class in our data, and
am happy to share fuller numbers or the corpus generator if y'all are interested.
All reactions