Releases: erigontech/mdbx-go
Release list
v0.44.0 — libmdbx v0.14.4
Vendored libmdbx moves from v0.14.3 (251562b2, 2026-08-09) to the v0.14.4 "Skynet Eve" (Накануне Skynet) release (2026-09-18), a bugfix release of the stable branch. The public C API is unchanged — the mdbx.h diff is documentation only — so the bindings needed no adaptation. The Go side gains the copy/defrag API and one env option.
Go API additions
Env.Copy,Env.CopyFlag,Env.CopyFD,Env.CopyFDFlagare back (#223), rewired through the unifiedmdbx_env_copy/mdbx_env_copy2fdentry points.Copy/CopyFDdo a plain as-is copy;CopyFlag/CopyFDFlagpassMDBX_CP_*through untouched —CopyDefaults,CopyCompact,CopyForceDynamicSize,CopyDontFlush,CopyThrottleMVCC, andCopyOverwritefor clobbering an existing target.CopyFDFlagcasts the descriptor in C, so it compiles and is tested on Windows too.Env.Defrag(#223), bindingmdbx_env_defragthrough a thin cgo helper, withDefragOptions(includingTimeLimit, which travels as 1/65536-second units) andDefragResult. The environment must be openedExclusive— cutting off trailing pages needs the whole-file lock; taking it per transaction is what tripsERROR_LOCK_VIOLATIONon Windows. Documented on the method.OptPresyncThreshold(#252), exposingMDBX_opt_presync_threshold.
Fixes
- A cross-thread
Txn.Abortpanic no longer leakscloseLock(#254).Txn.abortunlocked manually at the end, and in strict thread modestrictThreadCheckpanics by design when the aborting thread is not the one that began the txn — skipping thatRUnlockand leaving the read lock held forever, so any laterEnv.Closeblocked indefinitely. The panic is recoverable, so a caller could survive it and then deadlock on an unrelatedClose. Draining a txn pool begun on other goroutines is exactly that case. Now released withdefer, matchingTxn.ParkandTxn.Unpark.
libmdbx v0.14.4
- Windows: stricter placement checks on network, shared and layered volumes, to prevent database corruption and data loss. Upstream lists this as a backward-compatibility break: a database on such a volume can now fail to open with
MDBX_EREMOTE(ERROR_REMOTE_STORAGE_MEDIA_ERROR), notably with WOF active on compressed volumes. - Missing sub-page header check in
page_check(), which couldSIGSEGVwhile checking a corrupted database. - Regression returning
MDBX_BAD_TXNinstead ofMDBX_OUSTEDin particular cases. - Division truncation in
default_rp_augment_limit(), which yielded a too low and "stepwise" defaultMDBX_opt_rp_augment_limit. mdbx_get_sysraminfo()internals refined:sysinfo()plusMemAvailableon Linux,host_statistics64()on macOS.- Windows build fixes: CLANG/LLVM including Microsoft Clang,
scan4seq_neon()for ARM64, Win32 withMDBX_NATIVE_SEH=OFFwithout MASM,_except_handler4in 32-bitMDBX_WITHOUT_MSVC_CRT=ONbuilds.
One local fix on top of v0.14.4
v0.14.4's new macOS path passes HOST_VM_INFO_COUNT (15) to host_statistics64(HOST_VM_INFO64, ...), which wants HOST_VM_INFO64_COUNT (104) and returns KERN_FAILURE otherwise. On a straight v0.14.4, mdbx_get_sysraminfo() therefore fails with MDBX_ENOSYS on every macOS build, and libmdbx's own default_dp_limit() silently falls back to its worst-case estimate. mdbx-go carries the one-token fix, marked mdbx-go local fix in libmdbx/mdbx.c; it will be dropped once upstream takes it.
Build and tests
make cpre-vendors withgit archive(#253) instead of copying the working tree: only files tracked at the requested ref are vendored, build artifacts left in the clone stay out, and files deleted upstream are dropped instead of lingering.make cp LIBMDBX_REF=v0.14.4.- Rebuilding after a re-vendor needs
go build -a(#259, now in the README). Themdbxpackage pullslibmdbx/mdbx.cin through a cgo#includeacross package directories, which the Go build cache does not track — a plaingo testre-runs the old C. Verified: with the macOS fix above reverted,go test -count=1 ./mdbxstill passed. - Tests that begin a write txn now lock the OS thread (#256, #223). Without it the goroutine can migrate before the commit or abort, libmdbx returns
MDBX_THREAD_MISMATCH, the write txn stays alive andmdbx_env_closereturnsMDBX_BUSY— invisible off Windows untilsetup's cleanup started reporting theCloseerror.
v0.43.0 — libmdbx v0.14.3
Vendored libmdbx moves from v0.14.2 (530d0265, 2026-05-14) to the v0.14.3 "Китов" (Kitov) release (2026-08-09). No Go API changes — the only source edits are one constant remapping and Godoc.
Data-loss and hang fixes
- Copy without compaction produced a copy without payload. In v0.14.2 a non-compacting
mdbx_env_copybuilt the destination meta-pages from the empty model — the source tree roots and geometry were never carried over — so the copy opened cleanly and read as an empty database. Verified fixed here: a fill/copy/re-read probe overMDBX_CP_DEFAULTSandMDBX_CP_FORCE_DYNAMIC_SIZEround-trips the full payload, where the same probe read an empty database against v0.14.2. - Lost table content after aborting a nested transaction that dropped the table.
- Infinite loop in
mdbx_txn_abort()from amemcmp()/memcpy()typo. env_owned_wrtxn()bypassed locking underMDBX_NOSTICKYTHREADS— the mode mdbx-go always sets.- Major typo in
latch_maindb_locked(); missingreturnon an error path inmdbx_cursor_bind().
Spilling and cursors
Fixes concentrated in large write transactions: committing a pure nested transaction that has spilled pages; leak of the spilled-pages list on nested-transaction abort; tracking and invalidation of the inner part of sibling cursors; the cursor stack reworked around a page stash; spilling and accounting corrected for MDBX_AVOID_MSYNC=ON.
Leaks
mach_port_t in mdbx_get_sysraminfo(), Windows section handle in osal_mresize(), table name in dbi_open_locked(), cond_pair in copy_with_compacting().
Windows
ERROR_LOCK_VIOLATION during defrag in overlapped-I/O modes; lost global init and TLS destructors in MinGW static builds; /experimental:c11atomics workaround plus a compile-time guard against wrong MSVC codegen for C11 atomics on non-x86.
Go side
NoTLSnow maps toMDBX_NOSTICKYTHREADS. libmdbx removed the deprecatedMDBX_NOTLSalias, superseded byMDBX_NOSTICKYTHREADSsince 0.13. The constant staysDeprecated, keeps the same value and keeps existing callers compiling — preferNoStickyThreads.Env.Opennow documents a deadlock risk. As of libmdbx 0.14.x the env functions that need the writer lock but take no txn —SetFlags,SetOption,SetGeometry,Sync/SyncForce/SyncPoll,Stat/Info(nil),Close— acquire that lock when the calling goroutine does not own the in-flight write transaction. mdbx-go always setsNoStickyThreads, so calling one of them from a goroutine the writer is waiting on can deadlock. Make such calls from the writer's own goroutine, or while no write transaction is running.Env.SyncGodoc no longer namesMapAsync, an upstream-removed alias mdbx-go never exposed.
mdbx_replace_ex() changed upstream to return only previous data, never new — mdbx-go does not bind it.
Full changelog: v0.42.0...v0.43.0
v0.42.0
Go-side release. Vendored libmdbx is unchanged from v0.41.x — still v0.14.2 (530d0265).
Backward compatibility breaks
Txn.Unparkgains a result value (#236):Unpark() error→Unpark() (restarted bool, err error). Single-value assignments need_, err =.MDBX_RESULT_TRUEfrommdbx_txn_unpark— ousted and restarted on a newer snapshot — was previously collapsed into plain success while the cachedTxn.ID()kept the pre-restart value.CursorandTxncan no longer be copied (#250): both carry anoCopymarker, sogo vetcopylocks flags any copy. Copying either was always a bug — the copy would share the same C handle and double-close it. Embedding as a struct field is not a copy and stays fine.
New
Cursor.Open(txn, db)(#248) initializes an unopenedCursorin place, so callers may embedCursorby value instead of holding a*Cursor.Txn.OpenCursorkeeps its signature and delegates.Cursor.IsClosed()exposes the closed state that callers previously read off a nil*Cursor; it is nil-safe.
Fixes
Unparkreported errors under the op namemdbx_txn_park(#236).Park/Unparkcheckedenv._envwithout the close guard thatabort()takes, racingEnv.Close(#236).
CI
- golangci-lint config simplified,
nilnessbug-catcher added, workflows hardened (#244).
v0.41.2
Patch release on top of v0.41.1. Compile-time only — no runtime behaviour change.
noCopy on the C-handle types
v0.41.1 added Cursor.Open so a Cursor can be embedded by value, which also makes copying one easy to do by accident. A Cursor or Txn owns a raw libmdbx handle, so a copy hands two Go values one handle: closing either frees it while the other still points at it, and the second close is a double free into cgo.
| type | before | now |
|---|---|---|
Env |
already rejected — holds sync.RWMutex |
unchanged |
Txn |
unguarded | noCopy |
Cursor |
unguarded | noCopy |
go vet -copylocks now rejects the copy:
assignment copies lock value to b: mdbx.Cursor contains mdbx.noCopy
Embedding by value — the reason Open exists — is unaffected; noCopy bars copying a value, not holding one. sizeof(Cursor) is unchanged at 16 bytes.
Full changelog: v0.41.1...v0.41.2
v0.41.1
Patch release on top of v0.41.0. One change, backported from #248 via #249.
Cursor.Open — in-place initialization
Txn.OpenCursor always heap-allocates a *Cursor. Cursor.Open initializes an existing Cursor in place, so callers can embed one by value and drop a per-cursor allocation:
type MyCursor struct {
c mdbx.Cursor // by value, no separate allocation
}
if err := m.c.Open(txn, dbi); err != nil { ... }
defer m.c.Close()Close is still required, and Txn.OpenCursor is unchanged for existing callers.
IsClosed() reports the state callers previously read off a nil *Cursor.
Nil handling
IsClosed and Close are nil-safe, so var c *Cursor; c.IsClosed() reports closed instead of panicking — that is what lets IsClosed actually replace the old nil checks.
Open returns descriptive errors rather than dereferencing:
- nil receiver — there is nothing to initialize in place
- nil transaction — easy to hit when a caller has not begun its txn, and a cgo nil dereference says nothing useful
Not changed: Renew, Bind and Unbind dereference the same way. Pre-existing, and left for a follow-up.
Full changelog: v0.41.0...v0.41.1
v0.41.0
Headline: the cursor-leak fix (#229) plus a batch of correctness, API, and tooling improvements accumulated since v0.40.3.
⚠️ Requirements
- Go 1.25+ is now required (#243).
Bug fixes
- Cursor leak:
Cursor.Close()no longer skipsmdbx_cursor_closeafter a write txn ends — that leaked the C cursor allocation. Also hardens cursor lifecycle (nil/unbound guards,CursorToPoolunbind) (#229). (Backported to v0.40.3.1 and v0.39.19.1.) Env.Closenow surfaces themdbx_env_closeerror instead of leaking the handle on failure (#230).Txn.Resetnow returns themdbx_txn_reseterror (#235).
API / behavior
Env.CloseandTxn.Resetgain anerrorreturn — source-compatible for statement/defercall sites;*Envnow satisfiesio.Closer(#230, #235).- Added
OptDpReserveLimit; deprecated the misnamedOptDpReverseLimitalias (#234). Env.Info(nil)passes a NULL txn instead of opening a throwaway read transaction — fewer cgo calls, no reader-slot churn (#231).
Performance
Cursor.Getbuilds result slices directly from the C result, dropping the per-Txn scratch round-trip (#237).
Docs
- Corrected the zero-copy read semantics (no
RawRead) and removed stale finalizer claims (#233, #232).
Tooling / internal
- CI: erigon
make test-shortintegration job (#241); golangci-lint v2.12.2 with an expanded, curated linter set (#242, #243). - Removed dead callback machinery and stale comments (#239).
Full diff: v0.40.3...v0.41.0
v0.40.3.1 — cursor leak fix (backport)
Patch release on the v0.40.3 line backporting the cursor-leak fix (#246).
Cursor.Close() skipped mdbx_cursor_close when the write transaction had already terminated, on the LMDB-era assumption that libmdbx frees the cursor at txn end. It does not — txn end only marks cursors reusable; mdbx_cursor_close frees them. So every cursor closed after its write txn ended leaked its C-side allocation (an unbounded C-heap leak under cursor-heavy write workloads). Close() now always frees the cursor.
Minimal, low-risk backport (only the Close() change). Consumers pinned to v0.40.3 should upgrade to v0.40.3.1.
v0.39.19.1 — cursor leak fix (backport)
Patch release on the v0.39.19 line backporting the cursor-leak fix (#247).
Cursor.Close() skipped mdbx_cursor_close when the write transaction had already terminated, on the LMDB-era assumption that libmdbx frees the cursor at txn end. It does not — so every cursor closed after its write txn ended leaked its C-side allocation. Close() now always frees the cursor.
Minimal, low-risk backport (only the Close() change). Consumers pinned to v0.39.x should upgrade to v0.39.19.1.
v0.40.3
What's Changed
- mdbx: map MDBX_ENODATA to ErrNoData (hollow-cursor GET_CURRENT) by @AskAlexSharov in #228
- mdbx: expose Env.SyncForce and Env.SyncPoll by @AskAlexSharov in #227
Full Changelog: v0.40.2...v0.40.3
v0.40.2 range estimation/delete/distribution APIs
What's Changed
- Readers metrics and last master of libmdbx by @JkLondon in #216
- mdbx: expose ExactKeyValue/Pair navigation flags + merge last master by @JkLondon in #217
- new 0.14.2 mdbx release by @JkLondon in #218
- upgrade cgosymbolizer to latest by @AskAlexSharov in #219
- mdbx: replace interface{} with any (Go 1.18 alias) by @AskAlexSharov in #220
- Refresh README: erigontech path, libmdbx version badge, quickstart, sourcecraft links by @JkLondon in #222
- mdbx: expose Refresh / Checkpoint / CommitEmbarkRead / Rollback / Amend / Clone txn APIs by @JkLondon in #221
- mdbx: expose OptPrefaultWriteEnable by @AskAlexSharov in #224
- mdbx: expose range estimation + cursor distribution/deletion APIs by @AskAlexSharov in #225
Full Changelog: v0.39.18...v0.40.2