Том VI. Привязки (bindings)
Уровень: для разработчиков, работающих на языках, отличных от C/C++. Цель тома: вы знаете, как использовать libmdbx из своего языка, понимаете специфику и ограничения каждого биндинга. Честность: факты по конкретным обёрткам (репозиторий, версия, баги) требуют анализа их репозиториев и issues. Здесь приведены архитектурные следствия (NoStickyThreads, Send/Sync, GC/pinning, GIL — Global Interpreter Lock, глобальная блокировка интерпретатора в Python); позиции, требующие сверки с репозиторием, помечены явно.
Глава 34. Обзор экосистемы биндингов
Официально отслеживаемые биндинги: Rust, Go, Node.js, Zig, Python, .NET (C#), C++, Dart, Nim, Java, Haskell, Ruby, Scala. Плюс неофициальные (openresty/lua и др.).
| Биндинг | Язык | Статус | Подробнее |
|---|---|---|---|
| mdbx.h++ | C++ | Зрелый (официальный C++ API) | Гл. 40 |
| mdbx-rs / mdbx-sys | Rust | Зрелый/активный | Гл. 35 |
| mdbx-go | Go | Активный | Гл. 36 |
| python-lmdbx / mdbx-py | Python | Активный | Гл. 37 |
| node-mdbx | Node.js | Активный | Гл. 38 |
| mdbx-zig (lmdbx-zig) | Zig | Официально отслеживаемый | §34.2 |
| libmdbx-dotnet | .NET / C# | Активный | Гл. 39 |
| mdbx-dart / Isar | Dart (Flutter) | Активный (Isar использует libmdbx) | Гл. 41 |
| Nim | Nim | — | Гл. 42 |
| Java | Java | — | Гл. 43 |
| Haskell | Haskell | — | Гл. 44 |
| Ruby | Ruby | — | Гл. 45 |
| Scala | Scala | — | Гл. 46 |
Нюанс: карточки Гл. 42–46 (Nim, Java, Haskell, Ruby, Scala) — краткие; детали репозиториев требуют анализа (см. пометки в каждой карточке).
34.1. Что покрывает каждый биндинг
Типичная картина: биндинг оборачивает C API (mdbx_env_*, mdbx_txn_*, mdbx_dbi_*,
mdbx_cursor_*, MDBX_val) в идиоматику языка. Полнота покрытия различается: ядро (env/txn/
get/put/del/cursor) покрыто везде; продвинутые API (cache_get, defrag, clone, embark_read,
estimate_*) — не всегда. Сверяйте с документацией конкретного биндинга.
34.2. Zig (краткая справка)
mdbx-zig (репозиторий lmdbx-zig) — официально отслеживаемый биндинг для Zig.
Известные особенности: Zig активно используется для кросскомпиляции C/C++; для обхода багов
тулчейна может понадобиться MDBX_HAVE_BUILTIN_CPU_SUPPORTS=0. Отдельная карточка не выделена —
детали репозитория и версии требуют анализа.
34.3. Резюме главы 34
- Официально отслеживаемые биндинги: Rust, Go, Node.js, Zig, Python, .NET (C#), C++, Dart, Nim, Java, Haskell, Ruby, Scala; плюс неофициальные (openresty/lua и др.).
- Каждый биндинг оборачивает C API (
mdbx_env_*,mdbx_txn_*,mdbx_dbi_*,mdbx_cursor_*) в идиоматику языка. - Ядро (env/txn/get/put/del/cursor) покрыто везде; продвинутые API (cache_get, defrag, clone, embark_read, estimate_*) — не всегда.
- Зрелые/активные: mdbx.h++ (C++), mdbx-rs/mdbx-sys (Rust), mdbx-go (Go), python-lmdbx/mdbx-py, node-mdbx, libmdbx-dotnet, mdbx-dart/Isar.
mdbx-zig(lmdbx-zig) — официально отслеживаемый; для обхода багов тулчейна может понадобитьсяMDBX_HAVE_BUILTIN_CPU_SUPPORTS=0.- Карточки Nim/Java/Haskell/Ruby/Scala — краткие; детали репозиториев требуют анализа.
34.4. Чек-лист главы 34
- [ ] Выбрать биндинг по таблице главы и сверить его статус с репозиторием.
- [ ] Проверить, какие продвинутые API (cache_get, defrag, clone, embark_read, estimate_*) поддерживает биндинг.
- [ ] Для Zig при проблемах тулчейна учесть
MDBX_HAVE_BUILTIN_CPU_SUPPORTS=0. - [ ] Для кратких карточек (Nim/Java/Haskell/Ruby/Scala) проанализировать репозиторий биндинга до production.
Глава 35. Rust (mdbx-sys / mdbx-rs)
Обзор. mdbx-sys — низкоуровневые FFI-привязки к C API; mdbx-rs — безопасная обёртка
(идиоматичный Rust). Статус — активный/зрелый. (Детали репозитория требуют сверки.)
Установка. Через cargo: mdbx-sys = "...", mdbx-rs = "...".
Базовый пример (упрощённый, общий для типа):
use mdbx::{Env, Environment};
fn main() -> mdbx::Result<()> {
let env = unsafe { Env::builder().open("db.mdbx")? };
let txn = env.begin_rw_txn()?;
txn.put(b"greeting", b"hello, libmdbx")?;
txn.commit()?;
Ok(())
}
(Точный синтаксис зависит от версии биндинга — сверить с его README.)
Критические нюансы (из архитектуры libmdbx):
- Send/Sync. Транзакция и курсор привязаны к потоку (sticky). Безопасная обёртка должна
отражать это в типах: транзакция —
!Sync(использование из двух потоков =THREAD_MISMATCH). Для async-runtime (tokio и др.) обязателенMDBX_NOSTICKYTHREADS, т.к. future может продолжиться в другом OS-потоке. - Use-after-free после commit/abort.
MDBX_valуказывает в mmap; после завершения транзакции указатели недействительны. Обёртка должна ограничить время жизни значений временем транзакции (lifetime), а не передавать&[u8]наружу. - Блокирующие вызовы в async-контексте. Commit и запись блокируют поток; в async-runtime —
только через
spawn_blocking, иначе блокируется весь воркер.
Производительность. Накладные биндинга минимальны (FFI + ownership-проверки); чтение остаётся близким к C. (Сверка с бенчмарками биндинга.)
Пробелы. Зависит от версии: cache_get, defrag, clone и др. могут отсутствовать.
Резюме карточки. Двухслойная связка: mdbx-sys — низкоуровневые FFI-привязки к C API, mdbx-rs — безопасная идиоматичная обёртка (установка через cargo). Главные архитектурные следствия — sticky-транзакции (!Sync, MDBX_NOSTICKYTHREADS обязателен для async-runtime) и недействительность MDBX_val-указателей после commit/abort; блокирующие вызовы в async — только через spawn_blocking. Накладные расходы минимальны (FFI + ownership-проверки), продвинутые API (cache_get, defrag, clone) зависят от версии.
Чек-лист карточки.
- [ ] Для async-runtime (tokio и др.) выставить
MDBX_NOSTICKYTHREADS. - [ ] Убедиться, что обёртка ограничивает время жизни значений транзакцией (не выдаёт
&[u8]после commit/abort). - [ ] Блокирующие commit/записи выполнять через
spawn_blocking. - [ ] Сверить наличие cache_get/defrag/clone с версией биндинга.
Глава 36. Go (mdbx-go)
Обзор. mdbx-go — Go-биндинг через CGO. Статус — активный.
Установка. go get github.com/.../mdbx-go.
Базовый пример:
import "github.com/.../mdbx-go"
env, _ := mdbx.EnvCreate()
env.Open("db.mdbx", mdbx.CREATE|mdbx.NOSUBDIR, 0664)
txn, _ := env.BeginTxn(nil, 0)
dbi, _ := txn.DBIOpen(nil, 0)
txn.Put(dbi, []byte("k"), []byte("v"), 0)
txn.Commit()
(Сигнатуры сверить с репозиторием.)
Критические нюансы (из архитектуры):
- NoStickyThreads всегда выставляется. Go-горутина может мигрировать между OS-потоками, поэтому
биндинг открывает окружение с
MDBX_NOSTICKYTHREADS. - Один объект транзакции нельзя использовать из двух горутин одновременно — это гонка.
- Deadlock-риск: функции, требующие write-блокировку среды (
Env.SetOption,Env.Sync,Env.Stat,Env.Defrag,Env.Close), при активной write-транзакции в другой горутине могут взаимоблокироваться. Паттерн: одна транзакция — одна горутина, либо явная сериализация через channel/mutex. runtime.LockOSThreadнужен, если хотите зафиксировать горутину на OS-потоке (снижает риски при работе с TLS-слотом читателя).- CGO overhead — на каждую операцию границы CGO; для хот-путей группируйте операции в батч.
Пробелы. Полнота продвинутого API — сверять с репозиторием.
Резюме карточки. mdbx-go — активный CGO-биндинг (установка — go get). Горутины мигрируют между OS-потоками, поэтому биндинг всегда открывает среду с MDBX_NOSTICKYTHREADS; один объект транзакции нельзя использовать из двух горутин одновременно. Главные риски — deadlock на write-функциях среды и CGO-overhead на каждой операции, который лечится батчингом.
Чек-лист карточки.
- [ ] Одна транзакция — одна горутина (либо явная сериализация через channel/mutex).
- [ ] Не вызывать
Env.SetOption/Env.Sync/Env.Stat/Env.Defrag/Env.Closeиз другой горутины при активной write-транзакции. - [ ] При необходимости зафиксировать горутину через
runtime.LockOSThread. - [ ] Для хот-путей группировать операции в батчи (граница CGO).
Глава 37. Python (python-lmdbx / mdbx-py)
Обзор. Python-биндинг поверх C API. Статус — активный (сверка).
Критические нюансы (из архитектуры):
- GIL и блокирующие вызовы. Долгие read-транзакции и массовые записи держат GIL → другие потоки Python блокируются. Освобождайте GIL в биндинге при длительных операциях.
- Указатели в mmap действительны, пока жива транзакция. Python GC не управляет этим — нужен
явный context manager (
with env.begin(...)), чтобы гарантировать commit/abort вовремя. - fork() после import. При
multiprocessingвызывайтеresurrect_after_forkв дочернем процессе. - ctypes vs CFFI vs pybind. Выбор влияет на накладные; для батчей — минимизируйте число вызовов границы.
Пробелы. Сверка с репозиторием (Python 3.x версии, wheels, продвинутое API).
Резюме карточки. Python-биндинг поверх C API, статус активный (сверка). Ключевое — GIL: долгие read-транзакции и массовые записи блокируют другие потоки Python; время жизни mmap-указателей нужно гарантировать явно — context manager'ом (with env.begin(...)). При multiprocessing в дочернем процессе обязателен resurrect_after_fork; выбор ctypes/CFFI/pybind влияет на накладные расходы.
Чек-лист карточки.
- [ ] Гарантировать commit/abort context manager'ом (
with env.begin(...)). - [ ] Освобождать GIL в биндинге при длительных операциях.
- [ ] В дочернем процессе после
fork()вызыватьresurrect_after_fork. - [ ] Для батчей минимизировать число вызовов границы (ctypes/CFFI/pybind).
Глава 38. Node.js (node-mdbx)
Обзор. Node.js-биндинг (N-API). Статус — активный (сверка).
Критические нюансы (из архитектуры):
- Event loop и блокирующие вызовы. Синхронный C API блокирует цикл событий. Обёртка должна предлагать async-варианты (worker threads / libuv) для больших операций.
- Buffer ↔ MDBX_val. Node.js Buffer — владеющая обёртка; при передаче в libmdbx следите за временем жизни (не передавайте буфер, который может быть освобождён).
- NoStickyThreads — аналогично Go, если используется worker threads.
Пробелы. Сверка с репозиторием.
Резюме карточки. node-mdbx — активный N-API-биндинг (сверка). Синхронный C API блокирует event loop — обёртка должна предлагать async-варианты (worker threads/libuv) для больших операций; при worker threads нужен MDBX_NOSTICKYTHREADS (как в Go). Node.js Buffer — владеющая обёртка: следите за её временем жизни при передаче в libmdbx.
Чек-лист карточки.
- [ ] Большие операции выполнять через async-варианты (worker threads/libuv), не блокируя event loop.
- [ ] Не передавать буферы, время жизни которых не гарантировано.
- [ ] С worker threads выставлять
MDBX_NOSTICKYTHREADS. - [ ] Сверить возможности биндинга с репозиторием.
Глава 39. .NET / C# (libmdbx-dotnet)
Обзор. .NET-биндинг поверх C API. Альтернатива в экосистеме — LightningDB (не libmdbx! — проверяйте, что именно вы используете). Статус — активный (сверка).
Критические нюансы (из архитектуры):
- Pinning.
MDBX_valуказывает в mmap; если значение — управляемый буфер, .NET GC может его переместить. Решение:fixed/pinned, либо marshal-копия, либоSpan<T>. - SafeHandle для env/txn/cursor. Корректное освобождение через SafeHandle/finalizer.
- GC finalizer и порядок. Закрывайте в правильном порядке: env → txn → cursor; висячие хендлы
дают
BAD_DBI/BAD_TXN. - Непредсказуемое время жизни из-за GC — не держите длинные транзакции между сборками.
Пробелы. Сверка с репозиторием (версии, платформы, продвинутое API).
Резюме карточки. libmdbx-dotnet — активный .NET-биндинг поверх C API (сверка); не путать с LightningDB (это не libmdbx). Главное — pinning: MDBX_val указывает в mmap, а .NET GC может переместить управляемый буфер (fixed/pinned, marshal-копия или Span<T>). Освобождение — через SafeHandle/finalizer в порядке env → txn → cursor, висячие хендлы дают BAD_DBI/BAD_TXN.
Чек-лист карточки.
- [ ] Пиннировать управляемые буферы (
fixed/marshal-копия/Span<T>), не полагаясь на GC. - [ ] Освобождать env/txn/cursor через SafeHandle/finalizer в правильном порядке.
- [ ] Не держать длинные транзакции между сборками GC.
- [ ] Проверить, что используется именно libmdbx-биндинг, а не LightningDB.
Глава 40. C++ (mdbx.h++)
Обзор. Официальный C++ API (mdbx.h++), канон v0.15.0-263. Зрелый.
Иерархия классов:
env/env_managed— окружение;txn/txn_managed— транзакции;cursor/cursor_managed— курсоры;map_handle— таблица;slice/buffer— ключи/значения.- Управляемые варианты (
*_managed) — RAII: автоматическое закрытие при выходе из области видимости.
Нюанс:
slice— невладеющий,buffer— владеющий.mdbx::slice— лишь представление надMDBX_val(аналогstd::string_view): ссылка на чужие байты без копирования. Поэтомуmdbx::slice(std::to_string(k))в аргументе вызова безопасна (временная строка живёт до конца полного выражения, аinsert()копирует байты в базу синхронно), но хранить такой срез дольше оператора — висячая ссылка.mdbx::bufferпо умолчанию хранит собственную копию; режим «ссылка» включается явно (make_reference=true) — тогда действуют правила времени жизниslice.
Типизированные операции. Map-over-keys/values через трансляцию типов; параметры-структуры с fluent-установщиками (геометрия, режим, долговечность, reclaiming).
Исключения. Иерархия mdbx::error → mdbx::exception (от std::runtime_error) →
mdbx::fatal; 35 типизированных исключений (bad_map_id, db_corrupted, db_full,
key_exists, not_found, transaction_ousted и др.). Фатальные ошибки приводят к завершению.
Фрагмент из examples/c++/47-cpp-api.c++ — типизированные map-операции (insert/upsert через map_handle) и типизированные исключения (mdbx::key_exists и др.):
{
auto txn = env.start_write(); // RAII: сам закроется/откатится при исключении
auto ordinal = txn.create_map("ordinal", mdbx::key_mode::ordinal, mdbx::value_mode::single);
auto multi = txn.create_map("multi", mdbx::key_mode::usual, mdbx::value_mode::multi);
txn.insert(ordinal, buffer::key_from_u64(42), "answer");
txn.upsert(multi, mdbx::slice("tag"), mdbx::slice("x"));
txn.upsert(multi, mdbx::slice("tag"), mdbx::slice("y"));
// Типизированные исключения по коду ошибки.
try {
txn.insert(ordinal, buffer::key_from_u64(42), "again");
std::cerr << "FAIL: duplicate insert did not throw\n";
return EXIT_FAILURE;
} catch (const mdbx::key_exists &) {
std::cout << "duplicate -> mdbx::key_exists as expected\n";
}
txn.commit();
}
Полный код: 47-cpp-api.c++.
C++20. Концепты, аллокаторы (buffer<> с политиками владения), inplace_storage_size_rounding.
Пример:
Фрагмент (C++, иллюстрация): полная компилируемая версия — в
examples/c++/47-cpp-api.c++. Операции map-стиля (insert,
upsert, get, erase) — методы транзакции, а не таблицы.
#include <mdbx.h++>
int main() {
mdbx::env_managed env("db.mdbx",
mdbx::env::operate_parameters{});
auto txn = env.start_write();
auto db = txn.create_map("kv", mdbx::key_mode::usual,
mdbx::value_mode::single);
txn.upsert(db, "greeting", "hello, libmdbx");
txn.commit();
}
Проверьте точный синтаксис по вашей версии mdbx.h++.
Критические нюансы:
- Значение
upperдля новой БД движок выбирает сам (~золотое сечение ОЗУ, ограничено mmap-лимитом ≈140 ТБ на 64-бит);TOO_LARGE/ENOMEMвозможны при явно чрезмерном upper (например, под ASAN/Valgrind). Всегда задавайте геометрию явно. - Исключения vs коды возврата: выбирайте единый стиль;
mdbx::errorловится по типам.
Пробелы. key_mode::msgpack объявлен, но не реализован.
Резюме карточки. mdbx.h++ — официальный зрелый C++ API (канон v0.15.0-263): RAII-классы env_managed/txn_managed/cursor_managed, типизированные операции и 35 типизированных исключений через mdbx::error. Геометрию задавайте явно — upper для новой БД движок выбирает сам (~золотое сечение ОЗУ). Известный пробел: key_mode::msgpack объявлен, но не реализован.
Чек-лист карточки.
- [ ] Использовать RAII-варианты (
*_managed) для автоматического закрытия. - [ ] Всегда задавать геометрию явно (умолчательный
upperвыбирает движок). - [ ] Выбрать единый стиль ошибок: исключения
mdbx::error(ловятся по типам) или коды возврата. - [ ] Не использовать
key_mode::msgpack(объявлен, но не реализован).
Примеры к главе:
examples/c++/47-cpp-api.c++.
Глава 41. Dart (mdbx-dart / Isar)
Обзор. mdbx-dart — биндинг для Dart/Flutter; Isar — популярная локальная БД Flutter,
использующая libmdbx. Статус — активный.
Критические нюансы (из архитектуры):
- Isolate-специфика. Dart-изоляты — отдельные потоки; транзакции не пересекают изоляты (sticky thread). Каждый изолят — свои транзакции.
- Мобильные платформы. Встроенная БД на Android/iOS: аккуратно с парковкой длинных чтений и малым WAF (продление жизни flash).
Пробелы. Сверка с репозиторием.
Резюме карточки. mdbx-dart — активный биндинг для Dart/Flutter; на libmdbx построена популярная локальная БД Isar. Транзакции не пересекают изоляты (sticky thread) — каждый изолят работает со своими транзакциями. На мобильных платформах (Android/iOS) важны парковка длинных чтений и малый WAF (продление жизни flash).
Чек-лист карточки.
- [ ] Не передавать транзакции между изолятами (sticky thread).
- [ ] На Android/iOS аккуратно парковать длинные чтения.
- [ ] Следить за малым WAF для продления жизни flash.
- [ ] Сверить детали с репозиторием.
Глава 42. Nim
Обзор. Nim-биндинг. (Данные о репозитории и версии требуют анализа репозитория биндинга.)
Ожидаемые нюансы (из архитектуры): управление памятью (GC Nim не владеет mmap-указателями — нужны ручные ограничения времени жизни); привязка к потокам (sticky).
Резюме карточки. Nim-биндинг; данные о репозитории и версии требуют анализа репозитория. Ожидаемые нюансы: Nim GC не владеет mmap-указателями — нужны ручные ограничения времени жизни; транзакции привязаны к потокам (sticky).
Чек-лист карточки.
- [ ] Ручно ограничивать время жизни mmap-указателей (GC Nim ими не владеет).
- [ ] Держать транзакции в породившем их потоке (sticky).
- [ ] Сверить репозиторий и версию биндинга.
Глава 43. Java
Обзор. Java-биндинг (JNI). (Данные требуют анализа репозитория.)
Ожидаемые нюансы (из архитектуры): pinning критических буферов (JNI GetByteArrayElements
или direct ByteBuffer); освобождение native-хендлов (env/txn/cursor) в правильном порядке;
многопоточность (каждый поток — свои транзакции).
Резюме карточки. Java-биндинг через JNI; данные требуют анализа репозитория. Ожидаемые нюансы: pinning критических буферов (JNI GetByteArrayElements или direct ByteBuffer), освобождение native-хендлов (env/txn/cursor) в правильном порядке, у каждого потока — свои транзакции.
Чек-лист карточки.
- [ ] Пиннировать критические буферы (JNI
GetByteArrayElements/direct ByteBuffer). - [ ] Освобождать native-хендлы (env/txn/cursor) в правильном порядке.
- [ ] Не использовать одну транзакцию из нескольких потоков.
- [ ] Сверить данные с репозиторием биндинга.
Глава 44. Haskell
Обзор. Haskell-биндинг (FFI). (Данные требуют анализа репозитория.)
Ожидаемые нюансы (из архитектуры): чистота и эффекты (STM vs IO); привязка транзакций к OS-потокам; время жизни указателей в mmap.
Резюме карточки. Haskell-биндинг через FFI; данные требуют анализа репозитория. Ожидаемые нюансы: согласование чистоты и эффектов (STM vs IO), привязка транзакций к OS-потокам, время жизни указателей в mmap.
Чек-лист карточки.
- [ ] Разрешить чистоту/эффекты транзакций (STM vs IO).
- [ ] Учитывать привязку транзакций к OS-потокам.
- [ ] Ограничить время жизни указателей в mmap.
- [ ] Сверить данные с репозиторием биндинга.
Глава 45. Ruby
Обзор. Ruby-биндинг. (Данные требуют анализа репозитория.)
Ожидаемые нюансы (из архитектуры): GVL (глобальная блокировка VM Ruby — аналог Python GIL) и блокирующие вызовы; освобождение native-хендлов через finalizer/ensure.
Резюме карточки. Ruby-биндинг; данные требуют анализа репозитория. Ожидаемые нюансы: GVL (глобальная блокировка VM Ruby — аналог Python GIL) и блокирующие вызовы; освобождение native-хендлов через finalizer/ensure.
Чек-лист карточки.
- [ ] Учитывать GVL при длительных/блокирующих операциях.
- [ ] Освобождать native-хендлы через finalizer/ensure.
- [ ] Сверить данные с репозиторием биндинга.
Глава 46. Scala
Обзор. Scala-биндинг (через JVM). (Данные требуют анализа репозитория.)
Ожидаемые нюансы (из архитектуры): те же, что Java (JNI, pinning, хендлы), плюс особенности Scala-идиом (futures — аккуратно с потоками).
Резюме карточки. Scala-биндинг через JVM; данные требуют анализа репозитория. Ожидаемые нюансы — те же, что у Java (JNI, pinning, освобождение хендлов), плюс особенности Scala-идиом: futures требуют аккуратности с потоками.
Чек-лист карточки.
- [ ] Учитывать JNI-нюансы Java (pinning, порядок освобождения хендлов).
- [ ] В futures не рассчитывать на фиксированный поток.
- [ ] Сверить данные с репозиторием биндинга.
Итог тома
- Ядро биндингов (env/txn/CRUD/cursor) покрыто везде; продвинутое API — не всегда.
- Критические архитектурные следствия повторяются: NoStickyThreads (Go/async), Send/Sync и use-after-free (Rust), pinning/SafeHandle (Java/.NET), GIL (Python/Ruby), время жизни указателей в mmap (все).
- Карточки Nim/Java/Haskell/Ruby/Scala требуют анализа репозиториев — помечены явно.
Заключение учебника (тома I–VI)
Вы прошли путь от «что такое key-value база» до «почему GC-цикл деградирует при долгих читателях» и «как использовать libmdbx из любого языка». Освежите понятия по мере необходимости, проверьте свои конфигурации по чек-листам Тома V, главы 30, и измеряйте — числа без контекста не имеют смысла.