Skip to content

libdb 2026.09.9

Choose a tag to compare

@gburd gburd released this 19 Sep 19:28
· 42 commits to master since this release
v2026.09.9
f4c6c7d

A defect-fix release. Every fix here was found by a gate added in v2026.09.8 —
each of those three gates failed on its first run against master, which is what
they were built to do.

Fixed: --disable-mutexsupport had never built (U7)

Three stacked defects, each only visible once the previous one was fixed, which is why
a single error message understated it:

  1. src/dbinc/os.h declared prototypes without the types they name. It includes
    dbinc_auto/os_ext.h, which declares the atomic helpers in terms of db_atomic_t
    and atomic_value_t. Those live in dbinc/atomic.h, reachable only through
    dbinc/mutex_int.h — and mutex.h:12 includes that only under
    HAVE_MUTEX_SUPPORT
    . So the no-mutex build reached the prototypes with the types
    undefined.
  2. The no-mutex MUTEX_* stubs could not be used as values. They expanded to
    (mutex) = (mutex), an assignment expression. Upstream's comment explains that form
    exists so if-then-else blocks still parse — fine for statement use. But our os_aio
    cross-reap latch does MUTEX_TRYLOCK(...) == 0 at mp_sync.c:396, and comparing an
    assignment expression against 0 fails to compile. The stubs now expand to
    ((void)(mutex), 0) — valid as statement and value, returning the success code the
    trylock contract specifies, which is the right answer when there is no mutex to
    contend for.
  3. lock_stub.c was missing two SSI stubs. configure.ac:1168 substitutes
    lock_stub.o for the entire LOCK_OBJS set in a no-mutex build, so that file must
    no-op every __lock_* entry point the tree calls — 59 of them. Our SSI work
    added __lock_sicommit and __lock_sicleanup to lock.c but only
    __lock_sireap_lockers to the stub file.

That third one is the same shape as the exec-bit and manifest gaps this project keeps
finding: a hand-maintained second list with nothing checking it. The G14
configure-option sweep is now that check.

Fixed: DB_DIRECT_DB could not open a database (P2)

O_DIRECT requires a block-aligned transfer buffer. Every caller of
__fop_read_meta passed a plain stack array — u_int8_t mbuf[DBMETASIZE], 8 sites
across fop_rec.c and fop_util.c — carrying only scalar alignment. Measured:
0x7ffd3b755240, neither 512- nor 4096-aligned. The first metadata read failed
EINVAL, so no database opened at all.

Why this is worse than a configuration nobody builds. Whether the read fails
depends on the kernel tolerating an unaligned buffer for that particular call. I
measured DB_DIRECT_DB passing on one XFS/NVMe box and failing on another, both
with sectsz=512. So it is latent on a developer's machine and live on a deployment's
storage — and a green result proves nothing. An earlier note attributing this to
XFS-vs-other filesystems was wrong; measuring two boxes disproved it before the fix
was written, and that erratum is recorded.

Fixed at __fop_read_meta, the single choke point, so all eight call sites and any
future one are covered. Uses ALIGNP_INC (already in the tree) over an over-sized
stack buffer rather than an aligned heap allocation: nothing to free means no error
path can leak
, which matters on the database-open path in every process. Gated on
DB_ENV_DIRECT_DB, so the default build is byte-for-byte unchanged.

The behaviour test alone was not sufficient, and the release says so

flag_behaviour's direct_db mode asserts O_DIRECT is really set on the descriptor
— the right assertion, and it passes. But it passed before the fix too, on hardware
whose kernel tolerates the unaligned buffer. On such a box it cannot distinguish fixed
from broken.

So test/c/p2_align.c asserts the mechanism: that ALIGNP_INC over the over-sized
buffer yields a 4096-aligned address where the bare array does not, and that
DBMETASIZE still fits after rounding. It reports honestly when the bare buffer
happens to be aligned
rather than claiming a difference it did not observe. Declared
in test/MANIFEST so it cannot silently stop running.

Also in this release: the gates themselves (G12–G15)

Shipped in v2026.09.8 and worth restating, because they are why the two fixes above
exist:

  • A scaling-shape gate (test/bench/scale_shape_gate.sh) asserting monotonicity
    rather than absolute throughput, which is robust to runner noise. It currently
    FAILS at −68.2% at t=96 — correctly, because P1 is real and open.
  • A configure-option sweep covering all 54 options as swept / excluded-with-reason
    / failing, and checking every option is in one of those lists, so adding an option
    forces a testing decision.
  • Behaviour tests for six runtime I/O and durability flags that previously had zero
    test coverage.

Known issues, unchanged and open

  • P1 — the PGNO_BASE_MD allocation convoy. __db_new holds the metadata page
    write-locked until commit, across its own fsync, because __TLPUT is a no-op for a
    write lock inside a transaction. At t=96 bulk insert, 400 of 400 lock waits are on
    page 0
    ; new-order p99 is 2,064 ms against WiredTiger's 182 ms on the same
    device at the same page-read rate. This is the dominant cause of libdb's throughput
    peaking at 8 threads and falling to 32% of that peak by 96. Deliberately not
    rushed into this release
    : the metadata page holds the free list, so any fix is a
    two-phase-locking argument, and a wrong one corrupts databases.
  • P3 — DB_LOG_DIRECT has P2's defect at a different site. __log_write passes
    both a caller-supplied buffer and an arbitrary length to __os_io
    (log_put.c:1453), violating two O_DIRECT constraints, so it needs its own
    durability argument. direct_log remains XFAIL.
  • F1 — a transient Apple clang crash compiling env_register.c on macos-14
    (Abort trap: 6). An unmodified rerun of the identical commit passed and the job had
    succeeded on master's previous 5 runs, so it is not reproducible. Not filed upstream:
    a compiler-crash report needs the preprocessed source clang dumps into the runner's
    temp directory, and that runner is destroyed with the job. Recorded with the action to
    take if it recurs — capture the artifacts first, then file.
  • Unchanged: T1T6, S1 (opt-in DB_MPOOL_AIO), S5, U1U6, W1, B1.

Compatibility

No on-disk, log, region, or public-ABI change. __env_struct_sig() is 0xb86f77f0,
verified identical to v2026.09.8 with both arms measured in fresh build dirs; public
sizes remain 1744/552/2088/336. Existing environments attach unchanged. The
compatibility triplet stays 2026.0.9 and db_version() returns 2026,0,9, checked
against a built library.

Qualified from a pristine clone on a dedicated EC2 instance: default build, 32-bit
toolchain and meson available, --disable-mutexsupport build, flag tier (13 verdicts,
manifest OK), db tier, leak tier, exec-bit gate, and the manifest self-check.