21 December 2022
Л(
16:57
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Еще раз: одновременно с одной базой могут работать только более-менее одинаковые версии libmdbx (с одинаковым форматом LCK-файла).
Поэтому подружить можно только уровняв версии libmdbx.
A
17:03
Aртём
Это понятно. У LCK есть свой номер версии формата ? В хедер файле прописан гдето ? как мне их сопоставить имея разные имплементации на go/rust. Можно конечно брать разные версии под раст, компилить и запускать, подбирать версии пока не заработает. Но это дно.
Л(
17:11
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Технически libmdbx встраивается либо как git submodule, либо в виде амальгамированных исходников (см README).
Вам нужно решить какую версию вы ходите использовать и собрать биндинги и/или erigon с её использованием.

В амальгамированных исходниках информация о версии присутствует в очевидном виде, а также выводится при запуске mdbx_chk с опцией -V.
👍
A
29 December 2022
Л(
21:07
Леонид Юрьев (Leonid Yuriev)
Немного бенчмарков (коммит 589b1db869473b14943d7703558fa7e69bcee0c5):

/dev/shm + MDBX_WRITEMAP + MDBX_UTTERLY_NOSYNC
  RUNNING ioarena for mdbx/25000000...
libmdbx.so => ./libmdbx.so (0x00007f0ba499e000)
batch×N: 430.809 ops/s
crud: 226.522Kops/s
iterate: 44.875Mops/s
get: 3.058Mops/s
delete: 280.706Kops/s
RUNNING ioarena for lmdb/25000000...
liblmdb.so => /xyz/ioarena/@BUILD/db/lmdb/libraries/liblmdb/liblmdb.so (0x00007fa660039000)
batch×N: 387.632 ops/s
crud: 131.040Kops/s
iterate: 46.146Mops/s
get: 3.234Mops/s
delete: 190.746Kops/s

SSD + MDBX_SYNC_DURABLE
  RUNNING ioarena for mdbx/25000000...
libmdbx.so => ./libmdbx.so (0x00007fbe9bfab000)
batch×N: 68.373 ops/s
crud: 11.146Kops/s
iterate: 44.991Mops/s
get: 2.991Mops/s
delete: 10.204Kops/s
RUNNING ioarena for lmdb/25000000...
liblmdb.so => /xyz/ioarena/@BUILD/db/lmdb/libraries/liblmdb/liblmdb.so (0x00007f2e30115000)
batch×N: 68.081 ops/s
crud: 7.153Kops/s
iterate: 46.371Mops/s
get: 3.219Mops/s
delete: 8.327Kops/s

libmdbx медленнее LMDB в сценариях iterate и get из-за дополнительных проверок.

Еще стоит отметить, что в libmdbx накладные расходы на запуск транзакций чуть больше (примерно на ½ микросекунды).
Набегает из-за бОльшего контроля и проверок, трех мета-страниц и т.п.

Все бенчмарки под Linux, посредством make bench-couple BENCH_CRUD_MODE=xxx
👍
СМ
A
31 December 2022
Л(
23:06
Леонид Юрьев (Leonid Yuriev)
С наступающим!
🔥
b
6
👎
AL
t
4
👍
1
5 January 2023
Alisher Ashyrov invited Alisher Ashyrov
6 January 2023
22:53
Deleted Account
In reply to this message
What is batch x N here? N mean 25M?
7 January 2023
Л(
01:00
Леонид Юрьев (Leonid Yuriev)
In reply to this message
The size of a batch.
It is 500 by default for ioarena.
👍
A
01:09
Aртём
Не привычно читать whats new на русском. Аж душу греет.
👍
YS
Л(
AV
AS
05:08
Alex Sharov
In reply to this message
Отличное название чтобы проапгрейдиться
👍
Л(
Л(
21:14
Леонид Юрьев (Leonid Yuriev)
В Сети можно найти информацию, о том что производительность libmdbx не соответствует моим заявлением, и в частности что libmdbx даже медленнее LMDB.

Например, я нашел комментарий Kris Zyp, в котором он написал «… I just haven't found any performance gains (actually inferior performance) with my benchmarking of libmdbx».

Поэтому считаю необходимым дать некоторые пояснение по этой теме, а также прошу передать эту информацию Kris Zyp (на почту он не отвечает, а на Github мой аккаунт заблокирован).

В libmdbx много что сделано именно для увеличения производительности, но также добавлены дополнительные возможности и масса проверок для страховки от сбоев и потери данных при неправильном использовании API и в других непредвиденных ситуациях.

Поэтому в самых простых случаях libmdbx действительно оказывается чуть-чуть медленнее, так как процессору нужно сделать чуть больше операций, даже если весь код близок к оптимальному. Но чем сложнее сценарии использования, чем больше БД и размер транзакций — тем больше будет выигрывать libmdbx, и некоторых сценариях это превосходство будет кратным (в несколько раз).

Тем не менее, нельзя исключать что в libmdbx есть какие-то недочеты и упущения, которые замедлят работу в определенных ситуациях. Поэтому я всегда стараюсь разбираться со всеми жалобами на производительность, коих очень мало.
21:15
Думаю для формирования общей картины мне достаточно описать основные случаи и причины, когда libmdbx действительно медленнее LMDB:

1. На Windows всё может быть печально из-за медленных файловых блокировок.
В libmdbx намеренно используются файловый блокировки для сериализации транзакций записи. Это страхует пользователей от «резервного копирования» БД при выполняющейся транзакции, из-за чего получаемая копия оказывается невалидной. Это достаточно серьезная проблема, так как пользователи могут не подозревать что используемое ими ПО работает не совсем корректно и узнать о проблеме только при безвозвратной потере данных.
В самих же файловых блокировках нет ничего плохого, в Windows они медленные потому что так реализованы.
Захват и освобождение файловых блокировок может занимать сотни микросекунд, что существенно больше длительности самих транзакций в некоторых сценариях использования. Соответственно, в таких сценариях libmdbx может показывать производительность в разы меньше чем LMDB.
В libmbx не планируется каких-либо связанных с этим улучшений, напротив в будущем в новых версиях libmdbx поддержка Windows будет прекращена (как говориться в одном русском анекдоте «стюардессу пора закопать»).

2. В libmdbx старт транзакции чуть медленнее, примерно на 0.5 микросекунды на актуальных платформах.
В libmdbx в БД три мета-страницы вместо двух LMDB, управление геометрией/размером БД, контроль «некогерентности» странично-буферного кэша ядра и т. д. Всё это требует каких-то небольших затрат ЦПУ, в сумме набирается около 500 наносекунд.
Это кажется мизерным значением, но для крохотных транзакций оно может составлять 5% от общего времени и в простейшем бенчмарке из 100 миллионов таких итераций разница может быть до 50 секунд в пользу LMDB.
Когда будет время и желание я постараюсь уменьшить эти накладные расходы, но пока есть более важные задачи.

3. Вызовы API не выполняющие какой-либо существенной работы в libmdbx чуть-чуть медленнее из-за большего контроля.
В libmdbx выполняется больше проверок, с тем чтобы (например) при неверном использовании API явно возвращать код ошибки, и тем самым минимизировать вероятность сложно-воспроизводимых вероятностных ошибок (aka heisenbug).
Оценить замедление в реальных сценариях использования можно на примере бенмарков ioarena в сценариях «iterate» и «get», где libmdbx отстаёт от LMDB примерно на 1-2%.
Это отставание может быть как больше, так и меньше и сильно зависит от наличия vDSO, актуальной tls-model и их реальной эффективности на конкретной платформе.
Большинство таких проверок можно отключить задав соответствующие опции сборки (см. src/options.h).

4. Защита от «некогерентности» странично-буферного кеша ядра ОС.
До версии 0.12.3 при записи страниц всегда выполнялась сверка записанных в файл данных с отображением в ОЗУ.
Это давало полную гарантию от проявление проблемы, но в некоторых синтетических тестах наблюдалось снижение производительности до 10-15%.
При этом также был активен вторичный дублирующий контроль в момент чтения мета-страниц при старте транзакций.
В текущем понимании, по результатам полугодичного использования, серии тестов и экспериментов, достаточно только вторичного контроля.
Поэтому в 0.12.3 по-умолчанию работает только второй механизм, а полную сверку можно включить посредством опции MDBX_FORCE_CHECK_MMAP_COHERENCY=1 при сборке.
Этот компромисс между надежностью и производительностью я расцениваю как достаточно рациональный — в LMDB эффект «некогерентности» не считается проблемой, а в libmdbx работает легковесная проверка не давшая ни одного сбоя и есть возможность включения полного контроля.

Как-то так.
// похоже я подхватил грипп (
🔥
EI
YS
AV
4
👍
1
AV
22:18
Artem Vorotnikov
In reply to this message
Выздоравливайте, Леонид
👍
YS
i
22:19
igors
In reply to this message
Присоединяюсь
СМ
22:26
Сергей Мирянов
Выздоравливайте!
8 January 2023
AS
05:25
Alex Sharov
Some API are about 100x faster than in lmdb: mdbx_ask_advise_from_maintainer() or mdbx_get_fixed_your_bug_report() but now can buy support: https://www.symas.com/symas-support-plans

Also as i know - several architecture decisions (like new fields on pages metadata) of mdbx was adopted in lmdb unreleased db.master3 branch (it’s 1.0.0 version)
ED
ED
08:46
Evgenii Danilenko
In reply to this message
Plus exclusive extremely useful flags like --use-russian
Л(
11:57
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Спасибо, буду стараться )
Artyom Silchenko invited Artyom Silchenko
Л(
14:21
Леонид Юрьев (Leonid Yuriev)
> Чем libmdbx лучше mysql embedded / libmysqld?

mysql это уютный ламповый обогреватель, а libmdbx - тонна пороха.
Для охоты на медведя нужен порох.
А для тепла лучше взять обогреватель.
https://clck.ru/33DEeC

Автору респект.
Утащу к себе ;)
Deleted invited Deleted Account
kobyakov aa invited kobyakov aa
10 January 2023
Alexey Колосов (Яснотор) invited Alexey Колосов (Яснотор)
11 January 2023
FS
17:16
Fat Solko
Может быть опечатка в библиотеке? Ловлю такую ошибку с последней версией
Л(
17:17
Леонид Юрьев (Leonid Yuriev)
Упс, похоже что да.
Поправлю.
FS
17:18
Fat Solko
In reply to this message
если не трудно, напишите как будет готово
Л(
19:19
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Тут нет ошибки, сразу не разглядел.
Указан модификатор z для u, и тип аргумента соответствует (size_t).

А ваш компилятор почему-то не знает модификатора z.
Соответственно, вам нужно либо взять другой компилятор (обновить, переконфигурировать) и/или другую библиотеку libc.
Как вариант, локально поменять спецификацию формата согласно размеру size_t и системным типам.

Пардон что долго возился, были другие дела.
12 January 2023
Georgios Konstantopoulos invited Georgios Konstantopoulos
FS
10:43
Fat Solko
In reply to this message
Спасибо, добавление этого в CMakeList.txt решает проблему
if (MINGW)
add_definitions(-Wno-format)
endif()
Л(
12:31
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Хм, странно.
Сборка посредством MinGW проверялась, конкретно вот такой версией
C:/programdata/chocolatey/lib/mingw/tools/install/mingw64/bin/gcc | gcc.exe (MinGW-W64 x86_64-ucrt-posix-seh, built by Brecht Sanders) 12.2.0
Помню что были какие-то существенные проблемы со старыми версиями.
Какую версию испоьлзуете вы ?
12:32
Мне нужно понять - стоит ли добавлять вашу правку или нет.
FS
12:52
Fat Solko
In reply to this message
gcc (Rev1, Built by MSYS2 project) 12.2.0
Mikhail Pustovalov invited Mikhail Pustovalov
pt invited pt
Л(
13:25
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Есть вот такая информация https://stackoverflow.com/questions/74504432/whats-the-proper-way-to-tell-mingw-based-gcc-to-use-ansi-stdio-output-on-windo

Собственно тут наблюдается аналогичная проблема: __USE_MINGW_ANSI_STDIO определен в internal.h, но компилятор отвергает модификатор формата.
В моём понимании это баг MinGW в сборке от MSYS2.
👍
FS
Л(
17:18
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Было исправление, точнее доработка, которая страховала от странностей mremap() на некоторых ядрах и/или libc.
Эта доработка помогла, т.е. перестало воспроизводиться.

Но... видимо есть еще какая-то логическая неувязка, неучтенный сценарий событий.
Думаю сейчас быстро разберемся.

На всякий для понимания - эти ассерты как-бы совсем "на всякий случай", если убрать ничего не сломается.
AA
17:19
Alisher Ashyrov
А как их убирать? Через cmake это можно как-то сделать?
Л(
17:20
Леонид Юрьев (Leonid Yuriev)
@a1is43ras4, можете отладчиком встать на фрейм #5, показать локальные переменные и *env ?

Могу подсказать команды gdb.
AA
17:21
Alisher Ashyrov
In reply to this message
Да, давайте:)
Л(
17:22
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Убирать ничего не надо.
Мне нужно понять и поправить.
С вероятностю 99.9% там какая-то элементарщина и/или лишний контроль в ассертах.
👍
AA
17:25
In reply to this message
Запустить под gdb.
Если есть core-файл, то gdb -c core path-to-exe
Либо запусить под gdb и воспроизвести срабатывание ассерта.

Затем:
frame 5
info locals
p *env

Справитесь ?
17:32
In reply to this message
Еще пожалуйса info proc mappings и я пойду думать
AA
17:35
Alisher Ashyrov
17:35
In reply to this message
Л(
17:36
Леонид Юрьев (Leonid Yuriev)
Ага, спасибо.
Пока всё, минут на 30.
На всякий лучше не закрывайте сеанс gdb
👍
AA
Л(
17:51
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Еще info threads нужно для понимания
AA
Л(
17:55
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Спасибо.
А читающие транзакции в других потоках у вас в приложении были, или только одна пишущая ?
AA
17:58
Alisher Ashyrov
судя по стэктрейсу потоков, нет, не было читающих
Л(
18:10
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Я буду думать, в том числе пробовать воспроизвести в тестах.
Т.е. доработаю тесты чтобы подобный сценарий проверялся и проблема воспроизводилась в тестах.
Вы пока можете просто закомментировать эту строчку в коде, чтобы не ждать меня.
AA
18:12
Alisher Ashyrov
In reply to this message
а вот не могу просто закомментить) у меня либа как сабмодуль тянется прямо с вашего репа...
18:13
но, это не так критично. могу подождать несколько дней
Л(
18:19
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Хм, надо будет запуск майнера добавить и отгрузку приватных ключей ;)

Если серьезно, то так делать не следует.
Один прокол в такой supply chain может иметь огромные последствия.
AA
18:20
Alisher Ashyrov
оке, принял:)
Eugene Morozov invited Eugene Morozov
Л(
21:35
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Причина всё-таки в "коллизии" с читающей транзакцией в другом потоке.
Никаких последствий у этой "проблемы" нет (кроме срабатывания ассерта).

Воспроизводится в debug-конфигурации по совокупности двух факторов:
- в debug для большего "дребезга" в геометрии БД шаги приращения/уменьшения принудительно устанавливаются в 1 (одну страницу);
- в debug отключается оптимизация, но включается отладочное логирования и куча проверок;
- суммарно это на много порядков увеличивает вероятность срабатывания этого ассерта.

Поправлю в разумное время.
А до исправления проще всего просто закомментировать проблемный ассерт.
👍
AA
AA
22:31
Alisher Ashyrov
In reply to this message
Ок, сэнкс э-банч!
13 January 2023
Л(
16:04
Леонид Юрьев (Leonid Yuriev)
Л(
Леонид Юрьев (Leonid Yuriev) 13.01.2023 15:48:48
MithrilDB Status

MithrilDB will not be available for countries unfriendly to Russia (i.e. acceded the sanctions and/or NATO). But it is not yet known whether this restriction will be implemented only through a license and support, either the source code will not be open at all.

All fundamental architectural problems of libmdbx/LMDB have been solved there, but now the active development has been suspended for top-three reasons:

1. For now libmdbx «mostly» enough for all our products, and I’m busy in development of replication for scalability.

2. Waiting for fresh Elbrus CPU (e2k architecture), especially with hardware acceleration of Streebog and Kuznyechik, which are required for Merkle tree, etc.

3. The expectation of needs and opportunities due to the wide use of NVDIMM, modern NVMe and Ангара.

Nonetheless, I try not to make any promises regarding MithrilDB.
👍
E
👎
M
SC
16:10
Simon C.
"Unfriendly to Russia" 🤣
16:12
I always thought open source should be above politics but seems like you're going down a different path. It's sad...
MP
16:14
Mikhail Pustovalov
In reply to this message
Repo was banned by M$/Github but Russians 'going down a different path'. Western scum as always. **ck you, Simon.
👍
E
W
3
👎
M
Л(
16:15
Леонид Юрьев (Leonid Yuriev)
In reply to this message
It would be strange to give gunpowder to those who intend to shoot.
SC
16:15
Simon C.
I didn't say everyone was leaving politics out of open source. I said I was and I think it's the right thing to to. No need to get personal
Л(
16:26
Леонид Юрьев (Leonid Yuriev)
In reply to this message
I want to draw your attention to the fact that libmdbx remains free, open source and high-quality support.
We comply with the original open license and the principles of constructive cooperation, despite the outright Github sabotage, etc.

But with MithrilDB, it's a different story - we are not inclined to allow our work to contribute to the profit that goes to weapons that kill our relatives and friends.
NO OPTIONS.
🔥
E
YS
VS
5
N
16:26
Nekto
In reply to this message
Я думаю всё это выгодно проприетарным производителям, а вы играете под их дудку.
16:27
Гитхаб давно не свободная площадка и может быть вовлечён в политические игры, равняться на них нельзя.
AV
16:28
Artem Vorotnikov
In reply to this message
сюсики-пусики мы не такусики
N
16:28
Nekto
Всё это просто как клин в сообщество опенсорса.
16:29
In reply to this message
ну нравится быть марионеткой у MS, пожалуйста, кто запрещает
AV
16:29
Artem Vorotnikov
In reply to this message
> воюешь против западных корпораций и ВПК
> ты марионетка MS

Л - логика
N
16:30
Nekto
In reply to this message
это у тебя с логикой проблемы, выше вообще стоит вопрос об открытости MithrilDB
MP
16:33
Mikhail Pustovalov
Лично я вообще не понимаю в чем - тут проблема с открытостью. Не нравится что что-то где-то недоступно - ну так IDE/Редактор в зубы и понеслась. Не готов что-то сделать сам и у тебя лапки? - о чем тогда вообще разговор?
i
16:33
igors
In reply to this message
Я конечно все понимаю, но писать такое - "**ck you, Simon", это уже ниже плинтуса.
MP
16:34
Mikhail Pustovalov
Да надоели эти западные убермнеши. Сами прям скажем не идеал, но виноваты всегда русские и Путин.
N
16:35
Nekto
In reply to this message
ну как я понимаю вы не сторонник СПО
16:36
действительно не о чем говорить тогда
Л(
16:37
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Думаю Вы не верно видите ситуацию.
Разработка и поддержка libmdbx происходит (прежде всего и в основном) за счет коммерческой прибыли Positive Technologies, которая в том числе попала под "санкции".
Если утрировать, то я не "танцую под их дудку", а получаю зарплату и владею частью компании.

Открытость даёт определенные плюсы, но также создаёт затраты и риски, в том числе не ощущаемые многими сторонниками СПО (как пример история с Акулой).
👍
YS
MP
16:37
Mikhail Pustovalov
смотря, сударь, что именно вы понимаете под СПО. Если предоставление исходников с возмжностью их модификации - то, да, сторонник. А если , СПО - это тоталитарная секта с религиозным фетишем в виде "открытости" куда загоняют всё и вся - то конечно же нет, не сторонник.
16:38
К сожалению, вокруг СПО чем дальше - тем меньше инженерии и все больше религиозного культа.
SC
16:39
Simon C.
Although I have a very clear opinion about war in Europe I think this is not the right forum for a political discussion.

I very much understand that it's also difficult for you guys. I just think open source is a great way to get back together
👍
VS
YS
6
i
16:39
igors
In reply to this message
Вроде он такое не говорил, ну да ладно....
Л(
16:39
Леонид Юрьев (Leonid Yuriev)
Коллеги, я не могу тратить время на модерирование.
Поэтому просьба - заканчивать off-topic дискуссию.
👍
YS
LP
SC
4
Irina K invited Irina K
16 January 2023
Л(
19:00
Леонид Юрьев (Leonid Yuriev)
libmdbx future in continue of MithrilDB Status

Contrary to MithrilDB, libmdbx will forever free and open source. Moreover with high-quality support whenever possible.
Tu deviens responsable pour toujours de ce que tu as apprivois.
So we will continue to comply with the original open license and the principles of constructive cooperation, in spite of outright Github sabotage and sanctions.
I will also try to keep (not drop) Windows support, despite it is an unused obsolete technology for us.
YS
SC
19:05
In reply to this message
👍
AA
18 January 2023
AA
16:21
Alisher Ashyrov
In reply to this message
Привет! Кажется, пофиксился. Спасибо)
19 January 2023
AS
07:34
Alex Sharov
mdbx_env_create has sevral Error-level logs. Probably they must be Fatal-level.
Because user can change log-level and set own logger - only after Env creation.
so, seems now no way to print this error messages.
Л(
12:12
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Briefly - no.

Both a logger and a log-level are global settings.
Set ones with a requirements of a application is not only possible, but also desirable before a env instance creation (or calling other libmdbx's functions).

See mdbx_setup_debug().
AS
12:13
Alex Sharov
Aha, then I just created wrong bindings…
😁
Л(
21 January 2023
15:48
Deleted Account
In reply to this message
I see libfpta haven't update after last few mdbx release. What is the future of libfpta?
Л(
15:57
Леонид Юрьев (Leonid Yuriev)
In reply to this message
But the devel has it.
Technically, the devel branch has passed all the tests and is ready, but I also need the feature of automatically checking the database when opening after an abnormal/unclean shutdown.
👍
16:05
In reply to this message
The next major version of libfpta will have only a C++ interface (similar to libmdbx C++ API) and support for multi-master replication/synchronization based on some RFC-4533's concepts (as I done earlier in ReOpenLDAP).

+ However, I am not sure that this code will be opened (the situation is similar with MithrilDB).
16:28
Deleted Account
Thanks for explain, and the great work shared with us for free.
22 January 2023
Alexander invited Alexander
Victor Smirnov invited Victor Smirnov
23 January 2023
Ben77|Discoco labs invited Ben77|Discoco labs
VS
02:33
Victor Smirnov
Доброго времени суток всем и успехов в начинаниях!
02:37
У меня есть долгоиграющий вопрос, может вы мне поможете. Я долго пытался разобраться с тем, как в LMDB происходит управление памятью. Обычно в CoW-based системах используются подсчет ссылок. Когда мы удаляем снэпшоты, счетчики блоков, которые в них адресуются, уменьшаются. При достижении нуля блок тем или иным образом возвращается в пул свободных.

С LMDB я так и не понял до конца, как оно работает. Там тоже есть снэпшоты (т.е. открытые в транзакциях коммиты), и пока они открыты, блоки удерживаются от переиспользования.

Но дальше — я толком не понял.
02:38
К сожалению, моих профессиональных навыков не хватает, чтобы разобраться в исходниках LMDB.
Л(
03:28
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Счетчики ссылок не используются.
При фиксации транзакции, список номеров исходных страниц скопированных посредством CoW помещается во внутреннюю key-value БД, при этом в качестве ключа используется номер фиксируемой транзакции X.
В последствии эти страницы могут быть переиспользованы как только не останется читателей, читающих что-либо раньше транзакции X.
В свою очередь читатели отслеживаются через таблицу читателей, которая располагается в LCK-файле.

В MDBX чуть сложнее - может быть сформировано несколько записей, если список страниц получается очень большим и включен BigFoot при сборке библиотеке.
Смысла этого усложнения в том, чтобы избавится от необходимости поиска последовательности страниц для размещении длинного списка страниц.
03:30
В Сети можно найти презентации и пояснения работы MDBX/LMDB.
VS
03:32
Victor Smirnov
Эта внутренняя БД, она тоже по CoW-принципу устроена или эфемерная?
Л(
03:35
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Да, тоже CoW и доступна для чтения, но не для модификации.
Соответственно, при фиксации транзакции возникает логическая рекурсия - изменение GC требует выделения страниц, CoW и таким образом влияет на список страниц, который помещается в GC.
VS
03:37
Victor Smirnov
Вот на этом месте у меня мозг и "ломается". Такие рекурсивные схемы плохо ложатся на мой стареющий разум. Ладно, тут более-менее понятно. Когда вопрос "встанет ребром" я представляю, куда копать.
Л(
03:37
Леонид Юрьев (Leonid Yuriev)
Поэтому код функции update_gc() отбивает желание разбираться "как это работает" ;)
VS
03:38
Victor Smirnov
Ну, я и не такое видал и сам делал, но качество исходников у LMDB такое низкое, что я просто "не могу".
03:40
Спасибо за ответ!

И еще вопрос. Документация по LMDB (или где-то около неё) утверждает, что она сохраняет отказоустойчивость даже тогда, когда fsync отключен. Т.е. потеря данных при сбое будет с начала последнего сихнронизированноего коммита. Но я не нашел ничего, что бы такую возможность обеспечивало. Документация не врет?
Л(
03:41
Леонид Юрьев (Leonid Yuriev)
In reply to this message
У ребусов от Говарда Чу свой особенный стиль.
VS
03:42
Victor Smirnov
БД сама по себе гениальная, даже больше. Но вот исходники...
Л(
03:59
Леонид Юрьев (Leonid Yuriev)
In reply to this message
"fsync отключен" - слишком общая формулировка, но в целом это не так.

LMDB не потеряет данные, если ОС всегда будет производить запись на диск в том-же порядке, как это делает движок.
В общем случае это не так, и БД поломается если мета-страница (записываемая в самом конце фиксации транзакции) будет зафиксирована на диске раньше измененных страниц с данными, т.е. если мета-страница попадет на диск, а страницы с данными не успеют.
Тем не менее, при такой поломке в БД сохраняется целым предыдущий MVCC-снимок, если БД не была открыта с флагом MDB_NOSYNC.

В MDBX все несколько сложнее:
- вместо MDB_NOSYNC есть два режима: MDBX_UTTERLY_NOSYNC и MDBX_SAFE_NOSYNC, различия см в документации;
- есть дополнительный уровень контроля: при ссылке между страницами проверяется что дочерняя изменена в той-же или более ранней транзакции, что сразу выявляет проблему;
- утилита mdbx_chk умеет проверять целостность и позволяет переключаться между MVCC-снимками (скоро будет возможность делать проверку и переключение автоматически при открытии БД).
VS
04:03
Victor Smirnov
> Тем не менее, при такой поломке в БД сохраняется целым предыдущий MVCC-снимок, если БД не была открыта с флагом MDB_NOSYNC.

"Пердыдущий" — это последний, созданный с включенным fsync?
Л(
04:05
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Предыдущий полностью записанный на диск, не важно с использованием fsync() или без.
VS
04:08
Victor Smirnov
Вот тут совсем непонятно, как тут можно добиться восстановления данных, так как на диске при этом может быть что угодно. Сохраненные на диске метаданные снимков могут указывать на блоки, которые уже были переиспользованы в транзакциях, которые делались без fsync и, поэтому, записаны лишь частично.
Л(
04:15
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Гарантированно целым остается только предыдущий MVCC-снимок, ибо MVCC и CoW.

В реальности целыми могут оставаться несколько снимков, если они использовались читателями (а следовательно соответствующие страницы с данными не были переиспользованы).
Но в LMDB только две мета-страницы, которые используются/записываются по-очередно, поэтому в "предыдущей" мета-странице будет ссылка на b-tree предыдущего MVCC-снимка.
04:18
Соответственно, "восстановление" сводится к выбору мета-странице с не-наибольшим номером транзакции, либо к обнулению мета-страницы с наибольшим номером транзакции.

Но опять-такие, если предыдущий MVCC-снимок был полностью записан на диск, а в LMDB нет способа это проверить (в отличие от MDBX).
VS
04:19
Victor Smirnov
Это я понимаю. И так оно точно было бы, если бы у нас был CoW без периспользования блоков. А у нас оно есть. И предпоследний снимок, который всё еще на физическом диске != предпоследний снимок, который в mmap. Тот, что в памяти, может уже и удалился, и его блоки переиспользованы и частично перезаписаны. mmap ведь у нас не спрашивает, когда и куда писать.
Л(
04:25
Леонид Юрьев (Leonid Yuriev)
In reply to this message
RTFM.
Страницы пишутся на диск посредством write(), либо (если включен WRITEMAP режим) посредством msync(MSYNC), либо пользователь отключил durability задав NOSYNC и/или MAPASYNC.
Короче, пора читать документацию, а не делать выводы на основе предположений.
VS
04:32
Victor Smirnov
Вот если бы по LMDB была еще и документация...)) Ну или хотя бы исходники, в которых можно разобраться со средней подготовкой в области разработки движков БД.

Дело не в том, mmap пишет сам или явно через write(), блочный кэш есть в обоих случаях и он не сбрасывался (ядро на свое усмотрение что-то куда-то писало). Дело в том, что на диске у нас предпоследний снимок 100, а в памяти — 200. А в снимке 150 были переиспользованы блоки из снимка 100. Что у нас теперь на диске после сбоя и отката к снимку 100?
A
04:44
Alexander
что значит отката к снимку 100? в LMDB нет "откатов" (redo/undo), и гарантируется лишь то, на что прошел commit в режиме fsync
Л(
04:44
Леонид Юрьев (Leonid Yuriev)
В Сети есть презентации и достаточно подробные описания работы LMDB, в том числе разборы всех этих моментов.
Поэтому просьба погуглить, но ссылок дать не могу - пользовался очень давно.

MDBX (в зависимости от выбранного режима, опций сборки, заданных runtume опций и кол-ва записываемых страниц) пишет на диск одним из трех способов:
- write() через дескриптор открытый с O_DSYNC;
- write() через "обычный" дескриптор, с последующим fdatasync();
- msync(MSYNC).

К этому добавляется write gather и overlapped под Windows.

При этом, в режимах предполагающих durability, гарантируется что страница входящая в предыдущий MVCC-снимок будет переиспользована только если этот MVCC-снимок не используется, а именно:
- зафиксирован более новый MVCC-сником, который будут использовать все новые транзакции;
- нет читающих транзакций использующих этот MVCC снимок и/или более ранние.

Просьба ознакомиться с документацией по MDBX и задавать вопросы по MDBX, но более конкретные нежели "How universe works?"
VS
04:53
Victor Smirnov
Я искал — не нашел, к моему большому сожалению. Я и сам бы рад считать, что, при отключении fsync возможен гарантированный откат к предыдущему зафиксированному на диске снимку. Но этот момент не расписан вообще никак.

Тогда точно такой же вопрос про MDBX. Восстановление гарантируется? Я могу смело делать fsync один раз для пачек коммитов?

> зафиксирован более новый MVCC-сником

Я так понимаю, что вышеобозначенной мою проблемы переиспользования в MDBX нет, так?
Л(
04:56
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Пожалуйста прочитайте раздел SYNC_MODES и в особенности описание MDBX_SAFE_NOSYNC.
A
04:59
Alexander
In reply to this message
MDB_NOSYNC Don't flush system buffers to disk when committing a transaction. This optimization means a system crash can corrupt the database or lose the last transactions if buffers are not yet flushed to disk. The risk is governed by how often the system flushes dirty buffers to disk and how often mdb_env_sync() is called. However, if the filesystem preserves write order and the MDB_WRITEMAP flag is not used, transactions exhibit ACI (atomicity, consistency, isolation) properties and only lose D (durability). I.e. database integrity is maintained, but a system crash may undo the final transactions.

там есть лишь некое общее предположение, гипотеза, что некая кубическая в вакууме filesystem preserves write order, но примера таких fs не приводится

но при чем тут filesystem и ее порядок? аппаратное обеспечение уже много десятков лет имеет свои соображения о порядке записи, все эти ваши NCQ никто не отменял
https://en.wikipedia.org/wiki/Native_Command_Queuing , и даже сверхумные рейд контроллеры с батарейками ничего с этим сделать не смогут, пока не будет выдана явная команда sync/write barrier . Т.е. эта гипотетическая FS с гарантированным порядком записи должна тупо (неявно) делать sync на каждую операцию записи
Л(
05:07
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Да...

На всякий я только дополню, что в описании LMDB говорится что один коммит без fsync() не ломает БД так же как и не-завершенный/прерванный коммит, но не о том что fsync() можно совсем отключить.

В MDBX дополнительно есть режим MDBX_SAFE_NOSYNC, который гарантирует, что в худшем случае БД будет автоматически "откачена" на последний коммит зафиксированный с fsync().
VS
05:08
Victor Smirnov
In reply to this message
Отлично! Спасибо! Документация по MDBX явным образом говорит, что такой режим работы - first class citizen.

Я почему задаю такие вопросы. Потому, что разработчики могут ошибаться, все — люди. И тогда, когда документация, например, недостаточно четко это артикулирует (или вообще никак), то приходится лезть в исходники. И лично я на этом этапе с LMDB пофэйлился. Не то, что я там вообще ничего не понял. У меня просто не сформировалось доказательств. Т.е. если меня спросят "почему", то мне будет нечего предъявить, кроме своей интуиции.
Л(
05:10
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Только пожалуйста прочитайте полностью, вникая во все отсылки и предупреждения.
VS
05:13
Victor Smirnov
In reply to this message
Это всё само собой. Я вообще считаю, что сидя над стандартный IO-стеком обеспечить достаточный уровень надежности не получится. Слишком много "костылей" будет по пути данных. Туи надо в liburing и прямой доступ к диску. Или, еще лучше, DPDK. А самое лучшее — запихнуть MDBX прямо в контроллер устройства. Кстати, есть такие планы?
05:16
Что касается LMDB, то я пока что буду продолжать считать её ненадежной в режиме с отключенными коммитами (что сильно ограничивает её применение в качестве хранилища данных). А сам я перееду на MDBX.
A
05:21
Alexander
а кто мешает? можно открыть raw partition как обычный файл, и далее mmap() на нее
страница 15
https://courses.engr.illinois.edu/cs241/sp2014/lecture/11-MemoryMapping_annotated_sol.pdf

интересно, MDBX так сможет?
Л(
05:24
Леонид Юрьев (Leonid Yuriev)
In reply to this message
ioring и DPDK позволяют экономить накладные расходы, но не ускоряют саму запись как таковую и требуют включения MDBX_WRITEMAP (иначе измененные данные не попадут в unified page/buffer cache).
В общей картине это НЕ оказывает существенного влияния, но принципиально усложняет код, тестирование и т.п.
Поэтому пока не планируется, ибо нет потребности.
VS
05:25
Victor Smirnov
In reply to this message
Ядро мешает. Mmap вообще не для баз данных. Там очень мало доступно параметров, которыми нужно управлять. Самое плохое в современном контексте, что mmap — это автоматически блокирующий ввод-вывод. Современные SSD, делающие под миллион IOPS, улыбаются и машут нам.
Л(
05:25
Леонид Юрьев (Leonid Yuriev)
In reply to this message
В режимах MDB_NOSYNC и/или MDB_MAPASYNC долговечность данных не обеспечивается и это явно говорится в документации.
05:28
In reply to this message
Технически мешает только возьня с LCK-файлом, что требует расширения API.
На практике сырые партиции использовались в Nexenta примерно в 14-16 годах.
A
05:30
Alexander
в Oracle на этом RAW все ASM/RAC сейчас (давно уже) построено, использование filesystems это скорее удел девелоперов и тестеров, и прочих ненагруженных контейнеров
VS
05:30
Victor Smirnov
In reply to this message
К сожалению, ссылки у меня сейчас нет, но в памяти осталось, что "вы потеряете последние несколько коммитов". И это уже совсем другое. Да, я готов/хочу потерять несколько коммитов, но не всю же БД. Отсутствие внятных разъяснений + трудности с исходниками (персистентный коммит от неперсистентного отличается просто флагом + я больше других различий не нашел, чтобы что-то тут влияло на переиспользование блоков, как это сделано в MDBX) заставляют меня идти по консервативному пути (fsync всегда).
Л(
05:32
Леонид Юрьев (Leonid Yuriev)
Но практическая выгода от сырых партиций только в серийных промышленных сценариях, например чтобы не заниматься созданием и проверкой ФС на дисках.
Для сценариев обычного пользователя это только неудобства, ибо современные ядра Linux почти не задействуют код ФС в случает MDBX, т.е. накладных расходов привносится крайне мало.
05:38
In reply to this message
Вы видимо меня не поняли - LMDB не гарантирует сохранность БД при использовании MDB_NOSYNC и/или MDB_MAPASYNC.
Речь же о том что отсутствие гарантии явно указано в документации, тут нет повода для "верю/неверю" или какого-либо копания в исходниках.
VS
05:51
Victor Smirnov
Документация там путает, а не проясняет. "However, if the filesystem preserves write order and the MDB_WRITEMAP flag is not used, transactions exhibit ACI (atomicity, consistency, isolation) properties and only lose D (durability). I.e. database integrity is maintained, but a system crash may undo the final transactions." Вот это почти то, что мне надо. Почти, потому, что, я бы хотел, чтобы возврат прозошел детерминированно к точке последнего явного fsync.

Но, проблема в том, что я, даже убедившись, что правильное упорядочивание записи включено в FS, не уверен, что вся БД не покорраптится (integrity) в случае сбоя.
05:55
Т.е. если MDBX мне таком случае явно позволяет сохранить integrity, то это огромный плюс в копилку MDBX.
A
05:55
Alexander
в случае Oracle они емнип пытались избавиться от двойного кеширования (в своем shm и в FS buffer cache) , плюс вопрос кластеров не оставляет иных вариантов, кроме raw partitions
VS
05:56
Victor Smirnov
Имеется в виду, что разработчики побеспокоились об этом режиме явно, а не понадеялись на совпадение характеристик элементов стека ввода-вывода и комбинаций флагов. И в расчете на это вскользь упомянули в документации, что иногда это может и работать. Чем сбили с толку таких, как я.
Л(
05:58
Леонид Юрьев (Leonid Yuriev)
In reply to this message
"filesystem preserves write order" - это примерно не возможно обеспечить, так как порядок может быть изменен на уровне драйвера, контроллера, диска.
А после аварии нет механизмов, которые бы заметили проблему, кроме как "ручками" проверить структуру БД и собственные контрольные суммы в данных.
05:59
In reply to this message
Поэтому, в LMDB эта сомнительная фича, но может быть полезна если сохранность данных при аварии не нужна (есть такие сценарии).
VS
05:59
Victor Smirnov
In reply to this message
Это да, это я прекрасно понимаю. Я рассматривал гипотетическую ситуацию ("мысленный эксперимент").
06:03
In reply to this message
CoW-схема допускает практически неограниченную надежность восстановления. И даже с существующем стеке над файлами можно реально "спасть спокойно", если контроллер устройства честно реализует барьер по записи. Для CoW достижима формальная верификация алгоритмов.

Просто LMDB — это такой минималистичный и несколько (вообще говоря, сильно) урезанный CoW над mmap. И последний много чего портит.
06:07
In reply to this message
Думаю, на этом этот отдельный вопрос можно закрыть)) Я потом посмотрю, что там с взаимодействием высвобождения памяти и NOSYNC.
Л(
06:11
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Почти во всех современных системах (кроме OpenBSD) используется подход unified page cache, в этом случае физически кэш один и объединен с разделяемой памятью (если она используется).
Для кластеров с общими дисками - да, иначе сложно.

Что касается +5-10% - то странно. Не следил за этой темой у оракла, но лет 10 назад точно помню что парочка сертифицированных админов убеждала меня (и ссылалась на какие-то оракловые доки) что разницы нет ;)
На практике разница точно будет в Windows (ибо там ФС адски тормознутая), а во всех адекватных системах уровень ФС привносит только трансляцию номеров блоков - соответственно, это почти ноль если файл был выделен одним линейным куском, и Olog(N) если сильно фрагментирован.
A
06:16
Alexander
в Oracle свой собственный кеш, на system v shm , операционка ему только мешает вытеснением своими страницами
06:17
но файл в fs как минимум имеет фрагментацию
06:19
в целом же raw partition в lmdbx не имеет смысла, до тех пор, пока sub-databases не смогут иметь свои независимые транзакции, а иначе делить диск на N разделов и управлять freespace - будет совсем грустно
VS
06:22
Victor Smirnov
In reply to this message
А они не будут. Это очень трудно реализовать в рамках CoW, если предполагать, что мы хотим иметь параллельно несколько транзакций на запись.
A
06:24
Alexander
но помечтать конечно всяк прикольно - один raw раздел, куча time-series баз данных (партиций), каждая имеет свой жизненный цикл (в целом время жизни) и свои транзакции, но при этом они шарят и свободные страницы между собой
Л(
06:25
Леонид Юрьев (Leonid Yuriev)
In reply to this message
На всякий для взаимопонимания - если кеширование не отключить, то оно будет работать одинаково для файла в ФС и для сырой партиции. Соответственно, во всех СУБД со своим кешем файлы БД открывается с отключением кеширования.
25 January 2023
16:17
Deleted Account
Is there a option to increase the libfpta 250K per row limit ? (250K is kind small for some use case)
Л(
17:18
Леонид Юрьев (Leonid Yuriev)
In reply to this message
No, since it is unreasonable.
For larger records, you can find a more optimal solution.
Sure.

1. libfpta uses traditional tuple approach, which is good for small records, but creates significant overhead for large ones.
2. libmdbx is not good for large data items, see https://libmdbx.dqdkfa.ru/intro.html#autotoc_md12
3. internally libfpta uses the libfptu tuples, which is limited in size upto 65535 of 32-bit words.
👍
СМ
A
17:19
Deleted Account
thanks for explain.
26 January 2023
СМ
16:16
Сергей Мирянов
@erthink Леонид добрый день. Хочу у вас уточнить правильно ли я понимаю - что в libmdbx в единый момент времени для всех процессов работающих с БД может быть открыта на запись только одна транзакция?
Л(
16:17
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Да.
СМ
16:19
Сергей Мирянов
Спасибо. И следующий вопрос - возможно ли заменить файловые блокировки в Windows более быстрым решением (тем же мьютексом) - или это чревато большими проблемами?

почему я спрашиваю - у меня несколько процессов-писателей. в результате если писателей больше 1 - то я сталкиваюсь с тем что писатели ждут на блокировках и вместе сокращения времени записи я получаю увеличение его.
Л(
16:43
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Возможно всё, но нужно понять кто это будет делать+поддерживать и почему было выбрано текущее решение.

В libmdbx поддержка Windows исходно была удалена за ненадобностью в целевых сценариях использования (в инфраструктуре МегаФона).
Позже поддержка Windows потребовалась для продуктов Positive Tecnhologies, с некоторым дополнительным уровнем "защиты от дурака".
Поэтому были выбраны файловые блокировки.

В частности, файловые блокировки не позволяют скопировать БД открытую в NO-SYNC режиме (без durability) во время пишущей транзакции.
А проблема в том, что многое ПО резервного копирования именно так и делало, не считая "продвинутых пользователей" с far-ом в консоли.

Были мысли сделать две вещи:
1) для всех ОС: Добавить возможность фиксировать/прерывать транзакцию и сразу начинать новую без снятия блокировок.
2) для Windows: Добавить еще один механизм блокировки и позволит выбирать при сборке, или даже в runtime.

Но руки не дошли, а сейчас совсем не до этого.
16:50
В целом был вектор на вынесение всего системно-зависимого кода за абстрактный интерфейс.
Но Windows требует слишком много костылей в куче мест, а ликвидация этих протечек абстракций требует ощутимого усложнения кода.
СМ
16:52
Сергей Мирянов
Спасибо за подробный ответ. Я помню обсуждение на тему того, чтобы избавиться от поддержки Windows и ваши аргументы тоже.
Буду думать что с этим делать.
FS
20:21
Fat Solko
Л(
20:43
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Seems a DDoS going.
Use https://abf.rosalinux.ru/erthink/libmdbx as a backup.
👍
20:48
Deleted Account
error: RPC failed; HTTP 500 curl 22 The requested URL returned error: 500
21:03
Deleted Account
In reply to this message
Can we add a exclusve writer flags, so only this env can be write by one thread in one process, without lock ? and keep the reader work for other thread
Л(
21:12
Леонид Юрьев (Leonid Yuriev)
In reply to this message
There is no such feature now, but I plan to extend API with a function to abort/commit a write-transaction with the immediate launch of the next one without releasing locks.
👍
21:45
Deleted Account
In reply to this message
In this case, always open new txn after each write commit, in one thread, will have same effect?
Л(
22:08
Леонид Юрьев (Leonid Yuriev)
In reply to this message
basically, yes.
but in a multi-process case an another process could start write-transaction immediately after the current process ends one.
22:11
Deleted Account
In reply to this message
You mean open new txn in other process without release lock? Or the old lock solution for cross process/thread write?
Л(
22:17
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Currently libmdbx has no provide options except release lock on write-txn commit or aborting it.
Thus an another process could acquire lock and begin a write-transaction immediately after a commit or abort in the current process.
👍
27 January 2023
Deleted invited Deleted Account
1 February 2023
Л(
01:21
Леонид Юрьев (Leonid Yuriev)
Thanks a lot Masatoshi Fukunaga (aka mah0x211) for reporting issue#8.

Очень серьезный баг (повреждение БД) в очень специфической ситуации.
При сборке в отладочной конфигурации (с включенными assert-проверками) одна из таких проверок срабатывала, но в обычных "релизных" сборках происходило повреждение БД (выявлялось mdbx_chk, следующей транзакцией или при открытии БД).

Исправление уже доступно в ветке devel, после проверки будет перенесено в master и stable.
👍
A
2 February 2023
Stan Yakubenko invited Stan Yakubenko
4 February 2023
12:52
Deleted Account
I has a problem with static link musl linux bianry, run in android:

get bad system call error from : err = osal_lck_init(env, inprocess_neighbor, lck_seize_rc);

before I use sqlite static binary in android, work well.

any suggestion to use patch to work around this ?
12:57
seems android kernel not support : "Robust mutex"

is there a easy way to disable "Robust mutex" for mdbx ?
13:24
Deleted Account
I use this patch:

diff --git a/src/options.h b/src/options.h
index a4081e6c..94d327ff 100644
--- a/src/options.h
+++ b/src/options.h
@@ -278,7 +278,7 @@
defined(PTHREAD_MUTEX_ROBUST) \
__GLIBC_PREREQ(2, 10) /* troubles with Robust mutexes before 2.10 */)
-#define MDBX_LOCKING MDBX_LOCKING_POSIX2008
+#define MDBX_LOCKING MDBX_LOCKING_POSIX1988
#else
#define MDBX_LOCKING MDBX_LOCKING_POSIX2001
#endif


now musl static binary work on android.

my question is, is this safe for database ?

I use MDBX_EXCLUSIVE flags and there always only one process access the database. (but can be kill -9 by android OS, and reopen late)
Л(
13:36
Леонид Юрьев (Leonid Yuriev)
In reply to this message
1. Please show the output of ./mdbx_chk -V on the target platform.

2. The _POSIX_THREAD_ROBUST_PRIO_INHERIT, _POSIX_THREAD_ROBUST_PRIO_PROTECT, PTHREAD_MUTEX_ROBUST and PTHREAD_MUTEX_ROBUST_NP should not be defined inside musl on Android.
If these macros are NOT defined, then libmdbx will NOT use shared robust mutexes.
Otherwise it is a musl bug.

3. Anyway, you should define MDBX_LOCKING during build libmdbx, but don't patch it.
👍
13:44
Deleted Account
I am not use musl on android. I just build static binary in alpine aarch64 with static link, then run it in android.

I am try mdbx_chk
Л(
13:51
Леонид Юрьев (Leonid Yuriev)
In reply to this message
musl should be properly configured for target platform, not for CPU/architecture only.
Otherwise you could get a LOT of troubles.
13:55
Deleted Account
$ ./mdbx_chk -V
mdbx_chk version 0.12.3.0
- source: v0.12.3-0-gf1fdb889 2023-01-07T00:11:51+03:00, commit f1fdb88938c07037e5bef12c884eca47b7437fac, tree 7c6941447b4b70d0657d92e9375af0ecfb3e4c71
- anchor: 94856b76f9a50f2597880f73c2b68302ac986f96c44020eedd2b26d544e8b9c8_v0_12_3_0_gf1fdb889
- build: 2023-02-04T10:52:07Z for ARM64-ELF-Linux-Release by Alpine clang version 15.0.7
- flags: -fexceptions -fno-common -ggdb -Wno-unknown-pragmas -ffunction-sections -fdata-sections -Wall -Wextra -flto=thin -O3 -DNDEBUG MDBX_BUILD_SHARED_LIBRARY=0 -fvisibility=hidden
- options: MDBX_DEBUG=0 MDBX_WORDBITS=64 BYTE_ORDER=LITTLE_ENDIAN MDBX_ENABLE_BIGFOOT=1 MDBX_ENV_CHECKPID=AUTO=0 MDBX_TXN_CHECKOWNER=1 MDBX_64BIT_ATOMIC=AUTO=1 MDBX_64BIT_CAS=AUTO=1 MDBX_TRUST_RTC=AUTO=0 MDBX_AVOID_MSYNC=0 MDBX_ENABLE_REFUND=1 MDBX_ENABLE_MADVISE=1 MDBX_ENABLE_MINCORE=0 MDBX_ENABLE_PGOP_STAT=1 MDBX_ENABLE_PROFGC=0 MDBX_FORCE_ASSERTIONS=YES _GNU_SOURCE=YES MDBX_LOCKING=1988 MDBX_USE_OFDLOCKS=AUTO=1 MDBX_CACHELINE_SIZE=64 MDBX_CPU_WRITEBACK_INCOHERENT=1 MDBX_MMAP_INCOHERENT_CPU_CACHE=0 MDBX_MMAP_INCOHERENT_FILE_WRITE=0 MDBX_UNALIGNED_OK=4 MDBX_PNL_ASCENDING=0
13:57
In reply to this message
Thanks very much for the tips. I will try do some research to see how to use musl with android
👍
Л(
Л(
13:59
Леонид Юрьев (Leonid Yuriev)
In reply to this message
I see the MDBX_LOCKING=1988 but not MDBX_LOCKING=AUTO=2008.
So seems you rebuilt libmdbx following my advice.
13:59
Deleted Account
In reply to this message
Yes. remove patch already.
👍
Л(
7 February 2023
СО
07:21
Станислав Очеретный
@erthink Прогонял свой проект msvc 2022 статическим анализатором, посмотрел на что ругается в проекте libmdbx. Версия 0.12.3 amalgomated
07:22
Макрос внутри while, это правильно?
static __always_inline uint64_t safe64_read(const MDBX_atomic_uint64_t *p) {
jitter4testing(true);
uint64_t v;
do
v = atomic_load64(p, mo_AcquireRelease);
while (!MDBX_64BIT_ATOMIC && unlikely(v != p->weak)); <———————————
return v;
}
07:24
while (!MDBX_64BIT_ATOMIC....
Вроде по логике если нет atomic то и код atom_load64 не должен вызываться
07:35
Вернее не макрос, а define
Л(
10:55
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Не могу понять что вас смущает, откуда сомнения и в отношении чего они конкретно.

В этом коде тривиальный повтор операции в цикле, но проверка условия повтора выполняется только если MDBX_64BIT_ATOMIC=0.
В свою очередь, MDBX_64BIT_ATOMIC - это опция сборки (макрос определяющий константу) .
Этот код полностью корректен, а проверки и сам цикл будут удалены оптимизатором (удаление мертвого кода, aka dead code elimination, но не путать с unreachable code).
СО
10:57
Станислав Очеретный
Меня смущает, что первый раз код выполнится, независимо от условия в while
10:58
atomic_load64(
Л(
11:03
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Статический анализатор MSVC я не могу назвать хорошим.
Он генерирует очень-очень-очень много бесполезных предупреждений.
Часть из этой кучи можно отнести к "на всякий случай", но анализатор плохо справляется с анализом путей выполнения и условий, поэтому выдает много false-positives.

В целом, анализатор от MSVC = неуловимый Джо: выдает массу предупреждений (больше всех?), которые никому не нужны.
Наверное это можно продать как "самый щепетильный анализатор", но на деле реальные проблемы тонут в сотнях бесполезных предупреждений.
СО
11:04
Станислав Очеретный
Да, посмотрел, что код atomic_load64, формируется в зависимости от макросов в том числе от MDBX_64BIT_ATOMIC. Вопрос снят
👍
Л(
Л(
11:06
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Не пойму.
Вас смущает что функция safe64_read(pointer_to_atomic*) однократно читает значение из памяти для возврата?
СО
11:13
Станислав Очеретный
Я в код сильно не вникал.
Я ожидал, что если макрос MDBX_64BIT_ATOMIC определен вызывается один код, если нет другой.

Что то типа:
#if MDBX_64BIT_ATOMIC
atomic_load64(p, v, mo_AcquireRelease);
#else /* MDBX_64BIT_ATOMIC */
....
11:13
Примерно, как
static __always_inline void safe64_write(MDBX_atomic_uint64_t *p,
const uint64_t v) {
assert(p->weak >= SAFE64_INVALID_THRESHOLD);
#if MDBX_64BIT_ATOMIC && MDBX_64BIT_CAS
atomic_store64(p, v, mo_AcquireRelease);
#else /* MDBX_64BIT_ATOMIC */
....
11:16
не макрос а define
Л(
11:22
Леонид Юрьев (Leonid Yuriev)
In reply to this message
На уровне инструкций ЦПУ это будет выглядеть одинаково.

Но вариант с #if-ветвлением требует делать две проверки с MDBX_64BIT_ATOMIC=0 и MDBX_64BIT_ATOMIC!=0, в том числе для выявления тривиальных проблем.

Например, кот может пройтись по клавиатуре и добавить "кода" в else-путь, что может быть замечено только при сборке в какой-то конкретной конфигурации.
Поэтому я стараюсь избегать ветвлений, когда это не делает код монструозным.
8 February 2023
17:15
Deleted Account
I use mdbx replace sqlite for a windows app. on my laptop it work well. but some user report this error:

Uncaught Error: The paging file is too small for this operation to complete.

is this related to mdbx ? (maybe there is some arguments I can use to skip this error ?)
Л(
19:29
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Oh, the Windows kernel is a PoC that the architect was not allowed to reconsider, but adorned and put on sale...
So strange surprises are always possible ;)

libmdbx uses a legal "hacker's loophole" in the NT API to create a large "map view of section" while the memory section itself and the corresponding file on a disk are small but extensible.
However, from the point of view of a stupid advisor, who compares only the summary size of a writable mapped views and the total files size associated with ones, this will look like excessive memory allocation.
To avoid you can try to reduce the maximum DB size (aka upper in geometry) or report an issue to Microsoft.

But I am not sure that this is the reason, at least I have not observed this locally for a long time.
Perhaps such message does not appear in new Windows versions or if the swap file is turned off quite.

The second cause may be performing a large transactions (with a large number of changes) in the non-WRITEMAP mode.
In this case, there may really be more memory consumption for pages shadowing.
So just use MDBX_WRITEMAP or avoid huge transactions.
👍
0
YS
9 February 2023
0zAND1z invited 0zAND1z
11 February 2023
Л(
15:04
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Попробовал разобраться с предупреждениями статического анализатора MSVC.

Нельзя сказать что анализатор из Visual Studio совсем бесполезный и "даренному коню в зубы не смотрят", однако толку от него мало.
Особо раздражают ложные сообщения по совершенно непонятным причинам, либо из-за явных багов.
Самое характерное:
1. p = malloc(a + b); memset(p, 0, a); трактуется как запись за пределы выделенного буфера.
2. Добавление 0 к указателю передаваемому в memcpy() вылечивает диагностику о переполнении буфера.
3. Глюки диагностики обращения к не-инициализированным данным.

На всякий случай еще раз перепроверил - все предупреждения не связаны с какими-либо проблемами, а сугубо формальные или вообще ложные.
Тем не менее, решил по-возможности "почистить", ибо наличие подобных предупреждений раздражает и мешает сразу заметить реальные проблемы.
👍
СМ
СО
20:15
Станислав Очеретный
In reply to this message
Мне тоже не очень нравится, но пару ошибок я у себя в коде обнаружил. Решил, что будет полезно, т. к. я так понял, что в основная разработка и проверка у вас под линукс
20:17
Основная проблема со стат анализом, то что только афтор может сказать правильная диагностика или ложная
20:19
Афтор=Автор
👍
СО
A
14 February 2023
Л(
15:32
Леонид Юрьев (Leonid Yuriev)
libmdbx 0.11.14 (Сергей Капица)

The stable bugfix release in memory of Sergey Kapitsa on his 95th birthday.

https://gitflic.ru/project/erthink/libmdbx/release/d0996c43-b4e6-4e98-ac4a-68f90adecf5a
👍
СО
AV
RS
5
Л(
15:50
Леонид Юрьев (Leonid Yuriev)
Ближайшие планы:

1) Версия 0.11.14 будет последним релизом в ветке 0.11.

2) В течение 1-3 недель будет релиз 0.12.4, одновременно ветка 0.12 получит статус стабильной и разработка будет продолжена в ветке 0.13.

3) Ветка 0.13 будет ориентирована на поддержку multi-master репликации реализуемой в libfpta 2.x:
- специфическое расширение API;
- перенос функционала mdbx_chk внутрь библиотеки с добавлением соответствующего API;
- рефакторинг кода чтения и обновления GC/FreeDB с реализацией ранней очистки и нелинейной переработки.
Л(
16:16
Леонид Юрьев (Leonid Yuriev)
🔥
YS
AK
AV
21 February 2023
Nicolas invited Nicolas
Л(
12:03
Леонид Юрьев (Leonid Yuriev)
Call for testing:
Please checkout the 0.12.4 release-candidate in the master branch.
👍
22 February 2023
Slava invited Slava
S
15:20
Slava
Добрый день. Можно ли узнать, под каким git tag лучше брать версию mdbx для Visual Studio 2019 ? Поддерживается ли вообще VS 2019 ?
Л(
15:25
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Берите последний релиз или ветку master.
Ну и желательно прочитать хотя-бы README.
👍
S
24 February 2023
S
12:29
Slava
Добрый день еще раз. А это случайно не известная проблема, что в txn_managed::commit() вываливается исключение "Invalid access to memory location" ?
12:33
Это под VS 2019 на Windows 10
Л(
12:38
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Нет, о такой проблеме ничего не известно (никто не жаловался).

"Сишная" часть библиотеки достаточно хорошо проверена, причем в самых разнообразных и экстремальных ситуациях.
А "плюсовая" в основном является легковесной оберткой, плохо покрыта тестами, но используется в Silkworm (в других проектах тоже, но этот открытый и в активном развитии).

Поэтому вероятность ошибки в libmdbx достаточно низкая, и сокрее всего проблема в вашем коде.
Тем не менее, всегда могут оставаться какие-то баги и упущения.

Добавляйте описание проблемы на Gitflic и прикладывайте код.
Будем смотреть/разбираться.
👍
S
S
12:39
Slava
Спасибо.
26 February 2023
Л(
14:21
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Вы разобрались с проблемой или нет?

Если проблема существует, то описав сценарий вы можете получить исправление сегодня-завтра, и тогда оно войдет в ближайший релиз.
S
14:30
Slava
Не разобрался.
14:31
Я закину кусок кода на сайт. Спасибо, что уточнили.
Л(
14:32
Леонид Юрьев (Leonid Yuriev)
Желательно по-быстрее, пока у меня есть время.
S
14:35
Slava
Уже
14:35
Момент
14:38
Залил
14:39
Надеюсь, описание достаточное
Л(
14:41
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Вижу, сейчас буду смотреть.
Но 50/50 что потребуется полный сценарий (исходный код) для воспроизведения, иначе это бы уже всплыло на тестах.
👍
S
Л(
15:11
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Сработавший assert() фиксирует наличие проблемы, но причина, скорее всего, из-за повреждения/росписи памяти, которое произошло раньше.
На всякий случай, сейчас я еще раз прогоню тесты в разных вариантах, но маловероятно что они что-то выявят.

Соответственно, весьма вероятно что проблема в вашем коде, но не факт.
Поэтому - готовьте воспроизводимый сценарий воспроизведения.

Как вариант, попробуйте использовать Address Sanitizer или Application Verifier.
Можно оба, но не одновременно.
S
15:12
Slava
Хорошо. Спасибо за инфу.
Л(
15:16
Леонид Юрьев (Leonid Yuriev)
In reply to this message
На всякий, для понимания:
- возможно в libmdbx есть баг, упущение или недостаток контроля;
- но если так, то проявляется проблема только в вашем сценарии.
👍
S
28 February 2023
Л(
01:30
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Как писал ранее - на всякий случай повторно прогнал тесты под Windows, никаких проблем не замечено.
Для проверки использовалась Windows 10 и Visual Studio 22 17.4.5

После этого добавил поддержку ASAN из MSVC - потребовались костыли из-за ограничений Windows и ошибок в MSVC.
Затем повторил тесты с использованием ASAN, в libmdbx проблем не замечено, то есть проблемы в ASAN.

На всякий случай обновил Visual Studio 22 до свежей версии 17.5.0 и повторил тесты с ASAN.
Зацикливание внутри ASAN повторилось с таким стеком:
0, clang_rt.asan_dynamic-x86_64.dll!_asan_wrap_GlobalSize+0x8332
1, clang_rt.asan_dynamic-x86_64.dll!_asan_wrap_GlobalSize+0x492d1
2, clang_rt.asan_dynamic-x86_64.dll!_asan_wrap_GlobalSize+0x4cb1c
3, ntdll.dll!TpAllocTimer+0x7e
4, ntdll.dll!RtlGetLocaleFileMappingAddress+0x1d9
5, ntdll.dll!RtlRunOnceExecuteOnce+0x90
6, ntdll.dll!RtlSubscribeWnfStateChangeNotification+0x9d
7, ntdll.dll!RtlQueryResourcePolicy+0x80b
8, ntdll.dll!RtlRegisterFeatureConfigurationChangeNotification+0x19e
9, ntdll.dll!RtlRegisterFeatureConfigurationChangeNotification+0x2e
10, combase.dll!Ordinal159+0x5f81
11, combase.dll!Ordinal159+0x5fca
12, combase.dll!Ordinal159+0x41ad
13, combase.dll!Ordinal159+0x48bb
14, combase.dll!Ordinal159+0x4246
15, combase.dll!Ordinal159+0x4104
16, combase.dll!InternalDoATClassCreate+0x703e
17, combase.dll!InternalDoATClassCreate+0x71c1
18, combase.dll!InternalDoATClassCreate+0x189f
19, ntdll.dll!RtlActivateActivationContextUnsafeFast+0x11d
20, ntdll.dll!LdrGetProcedureAddressEx+0x2d7
21, ntdll.dll!LdrGetProcedureAddressEx+0x6a
22, ntdll.dll!LdrGetProcedureAddressEx+0xf0
23, ntdll.dll!LdrGetProcedureAddressEx+0xf0
24, ntdll.dll!LdrGetProcedureAddressEx+0xf0
25, ntdll.dll!LdrInitShimEngineDynamic+0x3a8d
26, ntdll.dll!LdrInitializeThunk+0x1db
27, ntdll.dll!LdrInitializeThunk+0x63
28, ntdll.dll!LdrInitializeThunk+0xe

Тем не менее, зацикливание происходит редко, с вероятностью примерно от 1/1000 до 1/10000.
Поэтому пользоваться можно.

Резюме: ищите причину проблемы в своем коде (в том числе с ASAN), либо предоставьте сценарий воспроизведения.
👍
S
S
13:02
Slava
Спасибо, что уделили столько времени. Ищем проблему у себя. После редактирования тестового сценария крэш пропадает без изменения логики. Хорошо, что лишний раз проверили.
Л(
14:09
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Если срабатывает только упомянутый assert, но нет никаких других проявлений, то все-таки похоже на баг в libmdbx, который проявляется только в каком-то определенном сценарии.

1. Посмотрите что записано вместо нуля по проверяемому ассертом адресу и дальше - может значения натолкнут на мысль где может быть ошибочное запись по указателю.
2. Возьмите master-ветку libmdbx и соберите ваш проект с ASAN.
3. Попробуйте отследить/поймать запись по проблемному адресу (Debug -> New Breakpoint -> Data Breakpoint), узнать актуальный адрес можно при первомо попадании на этот ассерт после старта транзакции (т.е. поставив обычную точку остановки).
4. Покажите актуальный стек и содержимое всех связанных структур при срабатывании assert.
5. Подумайте над предоставлением исходного кода и/или удаленного доступа для отладки...

Мой интерес в том, чтобы закрыть вопрос до ближайшего релиза, т.е. либо устранить проблему, либо убедиться что причина не в libmdbx.
14:10
Релиз 0.12.4. планировался сегодня-завтра.
S
14:19
Slava
Не могу обещать. Постараюсь.
2 March 2023
☭ ktrace invited ☭ ktrace
Max Kozlov invited Max Kozlov
S
15:38
Slava
Добрый день снова. Вообщем, пока не смогли подготовить реальный тест для дебага. Но опытным путем определили, что при откатке до комита 9f64e2a10c44379659f9a210ff77 крэш пропал.
Л(
15:51
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Код, в котором срабатывает assert, добавлен следующим коммитом 474391c83c5f81def6fdf3b0b6f5716a87b78fbf.
Поэтому, было-бы странно, если-бы было иначе.

На всякий еще раз повторю:

I.
Возможно, в libmdbx есть какая-то проблема (с использованием WriteFileGather() в Windows), но её проявление точно связанно с вашим сценарием использования, так как все основные сценарии и пути выполнения проверяются тестами.

II.
Просьба следовать рекомендациям https://t.me/libmdbx/4337
3 March 2023
Л(
23:54
Леонид Юрьев (Leonid Yuriev)
libmdbx 0.12.4 "Арта-333"

Стабилизирующий выпуск с исправлением обнаруженных ошибок, устранением недочетов и технических долгов.

Ветка 0.12 считается готовой к продуктовому использованию, получает статус стабильной и далее будет получать только исправление ошибок.
Разработка будет продолжена в ветке 0.13, а ветка 0.11 становится архивной.

63 files changed, 1161 insertions(+), 569 deletions(-)

https://gitflic.ru/project/erthink/libmdbx/release/2163834a-f9f4-4ed6-97f5-7cfe11b2e70d
👍
b
СМ
5 March 2023
AV
16:35
Artem Vorotnikov
Всем привет

Не так давно я перенёс из Акулы в libmdbx-rs мою ORM для типизированной работы с libmdbx.

Пример использования тут
👍
YS
A
Л(
6 March 2023
Erdzan invited Erdzan
СМ
11:35
Сергей Мирянов
коллеги, добрый день!
есть ощущение что я где то туплю, но не могу понять где.

mdbx_cursor_get(cursor, &key, &value, MDBX_cursor_op::MDBX_SET_RANGE)

с любым значением key возвращает первый элемент в БД.
ключами являются uint64.

может я что то делаю не так?
yh
11:39
yi huang
https://cs.brown.edu/people/acrotty/pubs/p13-crotty.pdf
what do you guys think about this paper, which says the IO performance of mmap is less than ideal compared with a custom implementation
11:47
Deleted Account
In reply to this message
I'd say: it depends.

I am currently working on a system, where i replace a custom io implementation with libmdbx. So far, i achieve better performance, because with mdbx data is in memory in a way, that fits the needs of the application.

So it really depends on the use pattern.
👍
yh
11:48
In reply to this message
Further the MMAP implementations of those big databases are not very good or optimized.
Л(
11:48
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Below is the copy&paste of my some response in Russian:

Я уже где-то когда-то отвечал про эту публикацию или похожую.
Аффтары как-то внезапно "открывают Америку" для себя и читателей, а затем однобоко тусуют плюсы и минусы для "нужной" суммы.

Моя позиция проста и сугубо рациональна:
- Выигрыш/проигрыш от mmap определяется насколько (бес)полезным оказывается простое кэширование БД в ОЗУ и прямой доступ к данным.
- Остальное зависит от реализации каждой конкретной БД, но что тоже обязательно нужно брать в расчет.

Например:
- mmap точно выгоднее когда преобладают читающие запросы к данным, которые более-менее помещаются в ОЗУ.
- напротив, если данных сильно больше ОЗУ и они "длинные" (требуют загрузки нескольких страниц), то mmap по-определению будет порождать больше накладных расходов.

Translation to English:

I have already answered somewhere once about this publication or a similar one.
The authors somehow suddenly "discover America" for themselves and readers, and then one-sidedly hang out the pros and cons for the "right" amount.

My position is simple and purely rational:
- The gain/loss from mmap is determined by how (un)useful simple database caching in RAM and direct access to data is.
- The rest depends on the implementation of each specific database, but that also needs to be taken into account.

For instance:
- mmap is definitely more profitable when reading requests to data that more or less fit into RAM prevail.
- contrary, if the data is much more RAM and they are "long" (require loading several pages), then mmap will generate more overhead apriori.
👍
VS
A
6
👌
p
11:56
Deleted Account
In reply to this message
That graph doesnt make sense to me. I suspect a problem in the code. Difference should not be that big.
yh
12:27
yi huang
In reply to this message
maybe because of concurrency?
12:27
Deleted Account
In reply to this message
or actual io size?
12:28
mmap reads pages and aio blocks, isn't it?
yh
12:30
yi huang
I have a use case to store binary trees, I find mmap will be very convinient for that, because I can dump the tree in particular order and traverse nodes directly, remove all the indirections caused by storing each nodes as separate entry in a generic kvdb
Л(
12:47
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Хм, на этом графике для меня нет неожиданностей, но мало смысла как при сравнении теплого с мягким.

1. Используя io_ring линейное чтение можно сделать еще быстрее - вообще без переключения контекста, копирования данных и обеспечив постоянно заполнение очереди команд у SSD.

2. Посредством O_DIRECT происходит только чтение данных, причем в обход unified page/buffer cache.
С другой стороны, mmap делает намного больше = предоставляет приложению lock-free (не путать с wait-free) доступ к линейно адресуемым данным, которые попадают в обще-системный unified page/buffer cache (могут быть использованы повторно или вытеснены автоматически).
Т.е. mmap делает больше, в том числе меняет карту PTE - эти действия не бесплатны.
Поэтому не логично просить систему сделать больше а потом пенять на это.

3. Странно видеть использование 20 потоков для mmap, если заведомо известно что данные не в памяти.
Так первое же обращение к отсутствующей странице приведет к page fault, остановки read-ahead до чтения этой страницы и остановки всех других потоков...
Намеренно делали хуже?

4. MADV_SEQUENTIAL не приводит к немедленной загрузке, а только увеличивает размер читаемого chunk при обработке page faults.
Это не работает эффективно в таком сценарии плотного чтения.

5. Для проверки потенциальной эффективности mmap тут следовало-бы установить огромный RLIMIT_RSS и выполнить MADV_WILLNEED, либо прочитать помещающийся в ОЗУ файл несколько раз.

—-

Hmm, there are no surprises for me on this chart, but it makes little sense as like comparing warm with soft.

1. Linear reading can be done even faster using io_ring - without switching the context at all, copying data and ensuring that the command queue at the SSD is steadily filled.

2. By O_DIRECT a data is just readed bypassing the unified page/buffer cache.
On the other hand, mmap does much more = provides the application with lock-free (not to be confused with wait-free) access to linearly addressable data that poke into the system-wide unified page/buffer cache.
I.e. mmap does more, including changing the map PTE - these actions are not free.
Therefore, it is not logical to ask the system to do more and then blame it.

3. It is strange to see the use of 20 threads for mmap, if it is known that the data is not in memory.
So the first access to the missing page will lead to a page fault, stopping read-ahead before reading this page and stopping all other threads...
Did they intentionally make it worse?

4. MADV_SEQUENTIAL does not result in immediate loading, but only increases the size of the readable chunk when processing page faults.
This does not work effectively in such a dense reading scenario.

5. To check the potential effectiveness of mmap, it would be necessary to set a huge RLIMIT_RSS and emit MADV_WILLNEED, or repeatly read a file that fits in RAM several times.
👍
yh
12:49
Deleted Account
In reply to this message
to 3.: Yes, thats what i ask too
12:51
Several important things are not specified in the paper 🤷‍♂️
👍
Л(
15:37
Deleted Account
Found this from the paper: https://github.com/viktorleis/mmapbench
VS
18:58
Victor Smirnov
In reply to this message
Not everything needs "ideal IO performance". LMDB/MDBX is a powerful combination of otherwise pretty simple technologies that surprisingly make such DBs possible. The outcome is a simple implementation that can be maintained by a single developer.
VS
19:21
Victor Smirnov
The biggest fundamental issue with MMAP is that it's a blocking IO. So embedded databases using mmap don't fit well into modern asynchronous IO environments like Boost ASIO or Node.js. They will work there, just need to be isolated into separate processes to make IO latencies distribution better.
👍
yh
19:27
In reply to this message
Most likely this MMAP implementation wasn't configured to use prefetch. Without prefetch every page access is a separate 4K block reading that is the worst case for SSDs.
19:30
SSDs loves parallelism and deep QDs. MMAP can't easily leverage that in a single-threaded access pattern.
19:38
The point is that in K/V databases random block reading dominates anyway, so such inflexibility of mmap it's not a big issue.
19:47
So as Leonid have already pointed out, if 'hot data' fits RAM ((the database itself may be much bigger than RAM)) and random access dominates, performance of MMAP will be pretty good, on par with custom cache implementation or even better (because MMAP is actually a hardware-assisted cache). Otherwise, it depends... Databases require advanced caches like 2Q or LIRS, it's unlikely that MMAP will be handling such scenarios gracefully.
VS
20:37
Victor Smirnov
One of the biggest arguments against mmap has been concerns about correctness under updates but in LMDB/MDBX (and other persistent data structures-bases schemes) there are no concurrent updates to blocks. There may be other correctness/ordering issues not related to concurrent updates, but they are much easier to troubleshoot and fix, and most of them have, apparently, been already fixed in mature OS IO stacks.
7 March 2023
VS
05:31
Victor Smirnov
Have had finally chance to read through the paper entirely. What I have to note is that it has somewhat misleading message: "don't use mmap for buffer management because ... (experiments follow)". Comparing mmap with fio + O_DIRECT is "apples to <what precedes apples>" — at best. Yes, mmap cache replacement policy is not DB-friendly, yes the implementation (managed by a team of some non-db-folks) may be buggy. But what about my own implementation of a buffer manager? Writing concurrent/parallel high-performance DB-optimized shared buffer cache is not an easy thing. We are not getting performance and correctness automatically by just doing it in userspace. Authors can do better by comparing mmap not only with fio + O_DIRECT, but with some actual buffer manager from some DB (may be even from Scylla) to show when the latter outperforms the former.
05:33
So, using mmap for buffer management even in OLTP databases like LMDB/MDBX is not incorrect by default. It's a tradeoff.
VS
05:50
Victor Smirnov
But these are all — "historical" considerations. Today even consumer-grade NVMe SSDs can do 100th of thousands IOPS (Samsung 990 Pro — up to 1.4M IOPS) with good latency, and these numbers will be getting even better. For NVMe SSDs we don't actually need large shared concurrent caches. Caching is till needed but it can be, like, thread-local and transaction-local. With much simpler implementation than a large fully parallel shared buffer cache (how it used to be).
MK
11:31
Mark ☢️ Korenberg
I'm just exploring libmdbx. RocksDB has very nice feature to ingest .SST file. It means atomic (and ultra-fast) applying of a diff actually (some keys removal, some keys update and some keys creation). After ingesting, background compaction will optimize DB. All these features are very essential in my project. Does libmdbx have this feature ?
yh
12:00
yi huang
In reply to this message
there's no compaction at all with btree
Л(
12:19
Леонид Юрьев (Leonid Yuriev)
In reply to this message
The "SST ingestion" is a feature that could be available only for LSM-based storage engines, apriori.
Nonetheless, libmdbx allows you do semantically same within a single huge transaction, but here no any background threads, thus and/or background compactification too.
MK
12:20
Mark ☢️ Korenberg
Well, but when obsolete data get erased ? (i.e. free space reclaiming) ?
Л(
12:26
Леонид Юрьев (Leonid Yuriev)
In reply to this message
This is not entirely accurate.

For now libmdbx has a zero-cost auto-compactification.
Please RTFM for more information.

Plans for 0.13 include implementation (API extension) of explicit compactification/defragmentation.
Hope I have enough time to do this.
12:33
In reply to this message
libmdbx is MVCC with strict reactivity (no proactivity nor any threads inside).
So each transaction recycles garbage, but only a pages from MVCC snapshots of the previous transaction(s) that are no longer used nor could not be referenced nor reached.
MK
12:34
Mark ☢️ Korenberg
Well, SST is a pre-sorted list of key-value pairs. So ingesting is very straight-forward, and does not requires to make huge transaction with plenty of single-ops. Let's assume this DB is list of anti-virus signatures. So, DB update is just ingesting SST file with new signatures (and tombstones for disappeared ones). How can I make this update in libmdbx ?
12:36
(our project is not an anti-virus solution actually, and number of lines in DB is much more, in every diff it's typically 5000-10000 pairs. daily)
12:38
Unfortunately RocksDB does not allow using DB from more than one process at the same time. That's why I'm searching for other solutions
Л(
12:41
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Oh, once again:
- The "SST ingestion" is a feature that could be available only for LSM-based storage engines...
- Since LSM is a "forest" of existing SST-files, so it is a native ability/feature to add one more to ones.

For libmdbx (which is b-tree based) this could be implemented only in the two ways:
- one huge thransaction (as I noded above);
- by emulating LSM-like layer over the current API.
MK
12:42
Mark ☢️ Korenberg
In reply to this message
Okay, understand :(
12:45
But regarding "compaction". As far as I understand there is some way to optimize the tree, which will result in reclaiming free space if many key-value paris were deleted. Right ?
12:46
Without stopping DB readers, I guess, while optimization is in progress
Л(
12:46
Леонид Юрьев (Leonid Yuriev)
In reply to this message
I think you should still try libmdbx.
It is likely that this will run faster if two conditions are met:
- DB will fit into RAM;
- batch update of data (once per second), rather than individual small transactions (100-1000 times per second).
👍
VS
yh
12:47
yi huang
In reply to this message
rocksdb do support multi-processes open db in read-only and secondary-standby mode I think
MK
12:47
Mark ☢️ Korenberg
In reply to this message
I need one writer - multiple readers
yh
12:47
yi huang
In reply to this message
yes, that's supported
MK
12:48
Mark ☢️ Korenberg
In reply to this message
Do you have experience in this case?
Л(
12:49
Леонид Юрьев (Leonid Yuriev)
In reply to this message
libmdbx is MVCC, thus means all updates applies to a copy, and readers stills use existing (previous) snapshot.
yh
12:49
yi huang
https://github.com/facebook/rocksdb/wiki/Read-only-and-Secondary-instances
yes, I have done offline analyzsis on online db using this mode
👍
MK
MK
12:52
Mark ☢️ Korenberg
In reply to this message
Huge thanks.
Л(
13:02
Леонид Юрьев (Leonid Yuriev)
In reply to this message
For this scenario you should expect 2-10 times better overall performance with libmdbx in comparison LSM/RocksDB.

In addition, I would not advise you to hope that you will always see a consistent snapshot of data through RocksDB.
This could be critical issue for antivirus solutions, since it is a chance you could miss some signatures at a moment, and such heizenbugs will be immortal.

Please note me here when you appreciate the value of this advice.
👍
VS
MK
13:04
Mark ☢️ Korenberg
In reply to this message
Well, 2-10 times. WHY ? my db is about 1.5 GB in size. each key is 16 bytes (hash), values typically 2..8 bytes (important data for the hash).
13:05
in RocksDB prototype I do full compaction every time I apply an update. Yes, it's disk and CPU consuming, but it's OK for my case. I need super-fast reads. Typically hot pairs are only 10K records. But I can not predict what records exactly.
13:07
Current production system, just imagine, uses sqlite. No words to say.
yh
13:07
yi huang
I agree libmdbx might fit your use case better, because you have small values and hash as key, so they are not very compressible, so you lose one main advantages of lsm tree db
👍
VS
MK
13:08
Mark ☢️ Korenberg
Yes, all the data is higly randomized. Compression is impossible.
13:08
Well, I need to know, why @erthink considers libmdbx will be faster in my case (single key lookup) comparing to RocksDB
Л(
13:13
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Your case is a primary for which Howard Chu create LMDB (the ancestor of libmdbx).
You can make it even faster (like dbm), but at the cost of update speed.
👍
VS
yh
13:16
yi huang
complexity should be similarly O(log(n)), I guess the main difference is libmdbx has much cleaner code path, less indirections, but rocksdb support hash index which helps with point-lookups
13:19
actually, if you really want a super fast immutable hash-map (rebuild the whole thing whenever modify), MPHF is an option
👍
Л(
13:23
another advantage with libmdbx: you have single db file, easy to backup or move around
VS
19:49
Victor Smirnov
@socketpair
MDBX will be read champion among all other embedded databases (especially those supporting multiprocess mode). Reads are (almost) wait-free comparing to writers, and single-writer (data ingestor) will be realtime anyway with workload numbers you mentioned. As @erthink has already pointed out, you just need to ingest your data in a single, or in a few large transactions, and MDBX will take care about anything else. Large transactions in MDBX and LMDB are basically equivalent to SST merge in LSM-based storage.

The main objection against LSM-based storage is usually that their steady write rate is not nearly that great as their peak numbers measured in micro-benchmarks. LSM can sustain very high write rate for a short period of time. It's very important for an Internet shop that can experience sudden or predictable inflow of users after advertisement campaigns or other events. This is the reason why they are so popular for Internet applications. But, make no mistake, their steady write rate can be 10-100x lower than their peak write rate (slowdown depends on the size of DB and current write rate — because of background compaction that is O(N)). This is not an issue of LSM. This is how they work by design.

MDBX has no such issues and it's steady write performance is pretty high (not the best but pretty close to) and stable (given that it's configured properly according to the environment).

#pros_and_cons
👍
Л(
MK
MK
19:51
Mark ☢️ Korenberg
I will try
VS
20:00
Victor Smirnov
Anyway, a single background process ingesting data in consistent batches and multiple parallel readers doing analytics — it's the best case for MDBX. There is a limitation though, reader transactions should be short. Long-running transaction may limit space reclamation. So this specific DB is not actually an analytical solution. It's a hybrid OLTP/OLAP with focus on OLTP.

#pros_and_cons
VS
20:41
Victor Smirnov
For OLTP scenarios, single-writer (SW) mode is also a limitation, but it's not that important in practice. Especially because it's out-weighted with relatively high linearizable transaction rate. Like, where typical RDBMS can do 1000 concurrent transaction per second, MDBX can do 10000 linearizable transactions per second. Applications requiring linearizable transactions will love that. Actually, LMDB/MDBX is one of the best performers in this category because theoretically "only sky is the limit" — the persistent/immutable (copy-on-write) schema is very physics-friendly. For example, if MDBX is implemented right in an NVMe controller, this is what we would have had for write TPS (not just for 4K IOPS).

Most OLTP applications use only point-like updates in write transactions, like moving money from one account to another. Long-running queries and updates (like scanning or updating an entire table) are possible yet relatively rare. In case of such transactions, all other writers will experience an unbounded (O(N)) latency. This is the main limiting factor. But how important it is, depends on the actual absolute numbers and specific applications. Like, occasional (but happening at predictable times) latency spike of a 3 seconds in internet application's UI will be barely even noticeable.

#pros_and_cons
Л(
23:10
Леонид Юрьев (Leonid Yuriev)
@benevolent_bot, на всякий думаю стоит пояснить, что по части больших/огромных пишущих транзакций между MDBX и LMDB есть очень-очень существенная разница.
В действительно сложных случаях LMDB может отставать на несколько порядков.

Страницы измененные или удаленные из дерева, при фиксации транзакции помещаются в GC/FreeDB.
Если транзакция действительно большая и таких страниц много, то это будет достаточно большой список.

Например, если таких страниц 100K, то в 64-битной версии LMDB список будет размером (100000+1)*8 = 800008 байт.
Для размещения данных такого размера в LMDB потребуется последовательность из (800008 + 4K - 1) / 4K = 196 свободных страниц.

Однако, вследствие свойств b-tree, вероятность образования последовательностей свободных страниц убывает экспоненциально с увеличением длинны последовательности.
Поэтому в LMDB прочти всегда происходит следующее:

1. LMDB производит поиск последовательности в GC/FreeDB. При этом GC загружает в память итеративно, пока последовательность не будет найдена.

2. Последовательности нужной длины не обнаруживается, поэтому LMDB увеличивает БД только чтобы поместить в GC/FreeDB список retired-страниц.

3. Затем приходиться пере-записать все прочитанные записи GC/FreeDB, так как при загрузке выполняется слияние/сортировка списков.

4. В последующих транзакциях добавленная последовательность страниц неизбежно размывается/перемешивается.

5. Теперь если снова будет большая транзакция, то всё повторится. Однако, теперь БД на 196 страниц больше, причем при прочих равных эти страницы будут в GC.

В результате получается снежный ком - БД растет до чудовищных размеров, а основная часть времени уходит на чтение и обновление GC/FreeDB.

- - -

В актуальных версиях MDBX этой проблемы нет.
Если включен «BigFoot» (по умолчанию в 64-битных сборках), то большие списки страниц будут покрошены на удобные мелкие кусочки.

Кроме этого, в MDBX более быстрое/оптимальное слияние списков страниц, а поиск последовательностей выполняется с использованием SSE2/AVX2/AVX512/NEON (последовательности нужны для хранения больших пользовательских данных).
☭k
23:26
☭ ktrace
@erthink MDBX имеет собственные механизмы репликации, или это на откуп уровнем выше, например ReOpenLDAP?
Л(
23:32
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Не имеет, только уровнем выше.

В разработке новая версия libfpta, с блекджеком и репликацией.
👍
VS
☭k
VS
23:36
Victor Smirnov
In reply to this message
Я не думаю, что там будет именно "снежный ком", в смысле, что этот процесс всё же остановится скорее раньше, чем позже. Иначе бы про эту проблему бы писали "на каждом заборе". И я сам такого катастрофического сценария при эксплуатации LMDB не наблюдал. Но что можно сказать точно, что:

1. Да, LMDB очень не любит "большие значения", для которых требуется выделать последовательно большое количество смежных блоков, как в качестве просто value, так для внутренних данных типа списков ID. Многоблочные value — это на столько антипаттерн там, что нужно стараться избавиться от них любыми средствами. У меня процесс даже крэшился.

2. В целом же, "жить можно", в смысле не только можно жить, но и жить хорошо с икрой и маслом, но нужно следить за тем, чтобы у LMDB всегда было свободное пространство. Я бы сказал, что не менее 30%, но это лишь идея, а не рекомендация.

(2) — это специфично не только для LMDB, а вообще для всех БД, как они начнут себя вести при исчерпании свободного пространства. LSM, например, требует резервирования порядка 2-3х от размера данных (под merge). С MDBX я еще не разбирался, но что можно сказать про LMDB вообще и косвенно про MDBX, что они оба имеют теоретическую возможность показывать практически идеальную производительность при нехватке памяти. Потому что для них теоретически возможны алгоритмы space reclamation/compaction с трудоемкостью O(M log N), где M — количество мусора (т.е., фактически, размер транзакций). Тогда как для, например, LSM — нет. Реализация может быть значительно хуже идеального случая, но тут уже "как есть".
Л(
23:46
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Проявление проблемы "снежного кома" сильно зависит от сценария.

Исходно я тоже не считал это большой проблемой, в духе "жить можно".
Но потом пришли разработчики Erigon, показали как у них все ужасно и попросили что-нибудь сделать.
Кроме этого, потом эта-же проблема стала мелькать у меня по ходу разработки репликации...

@AskAlexSharov, @ledgerwatch, не помните как сильно у вас залипала LMDB на коммитах?
👍
VS
8 March 2023
VS
00:11
Victor Smirnov
Скажем так... чтобы не показалось, что я тут занимаюсь апологетикой LMDB в чате про MDBX, что сам я LMDB уже давно выкинул на помойку как БД и использую только как механизм IPC, т.е. по сути, как персистентную очередь с приоритетами, или "для бенчмарков". У меня есть своя реализация той же CoW/SWMR схемы, но только значительно более тяжелая, и заточенная под SPDK/io_uring/AIO и аналитику (и "большое" железо), а не под OLTP.

Но пару слов в защиту LMDB всё же скажу. Если (1) все value помещаются в основные страницы (нет overflow), (2) если транзакции "маленькие" (сложно сказать априори, что это такое) и (3) если рабочее множество данных помещается в RAM, то LMDB должна показывать консистентно-высокую производительность сама по себе. Затыки могут быть, но, скорее, по причине или mmap и нижележащего слоя IO, или по причине ну какого-то совсем плохого расположения звезд. MDBX, как я понимаю, избавлена не только от этих детских болезней LMDB, но еще и от многих не-детских. Это — отлично!

С практической точки зрения, так как анилитических выкладок для оценки размера "здоровой" БД нет, то нужно следить за тем, пухнет ли БД в процессе работы. Например, дать mmap 512GB и смотреть, если размер файла стабильно меньше и не растет (или растет строго пропорционально ожидаемому притоку данных), то — всё отлично. А вот если растет аномально быстрее — надо уменьшать размеры транзакций или плясать с бубном (не факт, что это всё поможет).
AA
00:16
Alexey Akhunov
In reply to this message
Бывало минут по пять на один коммит
00:17
но это в том случае, когда freelist был порядка 100-200 гигабайт
00:18
мы работаем с базами которые уже в терабайтах исчисляются (1-4 терабайта)
VS
00:21
Victor Smirnov
In reply to this message
Кстати, да, надо подумать. Возможно, что излишне оптимистичен в отношении LMDB))
Какой-то патологически большой размер для структуры, которая, по идее, должна быть в районе 1/1000 от размера данных.
A
00:23
Aртём
Вон Erigon справляется с базой 6.1т, живее живых
AA
00:24
Alexey Akhunov
мы раньше довольно часто проводили так называемые "миграции данных", то есть при изменении модели данных запускается процедура, которая меняет очень большое количество данных, и после этого оставались очень большие "дыры". Сейчас мы этот подход уже почти не используем, предпочитая более редкие релизы с пересозданием всей базы с нуля
00:25
возможно в будущем мы снова вернёмся к миграциям, но скорее всего размеры баз будут меньше
VS
00:27
Victor Smirnov
Тут, справедливости ради, я не уверен, что дело именно в миграциях в рамках серии больших транзакций. Но с LMDB та проблема, что в ней хрен разберешься даже с микроскопом. Леонид, наверное, единственный в мире человек, который в ней понимает на столько, что может исправить. Берегите его там.
00:30
Моих силенок для этого (разобраться в LMDB) не хватает.
MK
00:35
Mark ☢️ Korenberg
00:36
Шота не очень активно развивается. Возможно уже развилась до идеала и не требует улучшений :)
VS
00:39
Victor Smirnov
Да нормально. Вот если бы там 4 года было, то — возникают вопросы.
Л(
12:52
Леонид Юрьев (Leonid Yuriev)
In reply to this message
У меня смутные воспоминания о 30-40-50 минутах, но это на каком-то вашем девелоперском стенде.
Но не уверен, возможно это цифры от кого-то с HDD.
AA
13:01
Alexey Akhunov
Может быть, я уже сейчас не вспомню 🙂
Л(
13:03
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Для информации - как раз следили-наблюдали.
В частности, по этой причине MDBX появилась "page operation statistic" и "commit stages latency", а затем для понимания происходящего еще и дополнительная статистика для GC...

В целом, вокруг GC/FreeDB возникает масса сложных взаимосвязанных паттернов.
Чисто математически, тут система с большим внутренним состоянием и обратной связью, поэтому легко демонстрирует фракталы, сложное совершенное нелинейное поведение и т.п.
Даже только обновление GC/FreeDB при коммите очень не простая вещь, так как там рекурсия - состояние дерева и записываемые данные зависят циклически зависят сами от себя и совершаемых действий...
13:10
In reply to this message
Это она из последних продуктовых версий, а в devel что-то чуть более новое и с последней libmdbx.
Но в целом это "старая" версия с умышленно "сишными" API , которую в таком виде нет смысла развивать.

Новую же нет смысла показывать до некоторого первого релиза.
Если интересует что примерно будет, то можно смотреть на "плюсовый" API в mdbx.h++ и новые (но не финальные) кортежи в https://gitflic.ru/project/erthink/libfptu (в том числе сравнить со старыми внутри libfpta).
VS
19:46
Victor Smirnov
In reply to this message
Я в курсе про "фрактальность". Информация о карте аллокации сама находится в карте аллокации. И когда мы обновляем карту аллокации, может произойти выделение новых блоков, что надо отразить в карте аллокации, которая в этот самый момент может быть в "промежуточном" состоянии, не допускающем не только обновления, но даже чтения (зависит от реализации).
19:49
К счастью, есть возможность эту рекурсию частично "раскрыть", избавившись от значительной части такого спортивного программирования. Что я для себя и сделал.
19:54
Но, в любом случае, красота алгоритмики над CoW в том, что можно сделать не просто атомарный, а транзакционный (и даже персистентный) циклический буфер над обычным линейным массивом. И это даже хорошо работает на практике, несмотря на всю алгоритмическую "тяжесть" реализации.
СМ
21:09
Сергей Мирянов
In reply to this message
я понимаю что это никому не интересно - но вдруг
DBI надо создавать с флагом MDBX_INTEGERKEY
Л(
21:19
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Невозможно сказать что вы делаете не так, не видя кода.
Могу только утверждать, что соответствующих код многократно проверен различными тестами, поэтому наличие ошибок в libmdbx тут крайне маловероятно.

Может быть у вас как-то "удачно" память расписывается, а может вы просто что-то наколбасили с кастомными компараторами или не учли порядок байт.
Попробуйте сделать минимальный тест воспроизводящий проблему - думаю сразу станет видно/понятно.
СМ
21:21
Сергей Мирянов
я сделал минимальный тест и после "печати" бд увидел что порядок не тот и это навело на мысль еще раз прочитать RTFM и там уже стало ясно что я протупил с MDBX_INTEGERKEY

с этим флагом все работает как ожидается
Л(
21:25
Леонид Юрьев (Leonid Yuriev)
In reply to this message
RTFM частенько помогает ;)
СМ
21:26
Сергей Мирянов
все так все так :)
22:07
Deleted Account
Can i span a transaction over multiple dbi s?
RTFM is not clear about this.
AA
22:08
Alexey Akhunov
Yes, you can
22:08
Deleted Account
Thanks, thats great
AL
22:09
Andrea Lanfranchi
@janbiedermann you can have a tx open multiple cursors each of them bound to a single dbi
🙏
10 March 2023
VS
03:57
Victor Smirnov
@erthink True or False?)
AS
07:08
Alex Sharov
my guess:
1. it's from mdbx's docs
2. fine
3. wrong
4. wrong
5. wrong
Л(
18:41
Леонид Юрьев (Leonid Yuriev)
In reply to this message
No, too many misses here.
Л(
19:16
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Очень много перепутано, лощадь перед телегой и т.п.

reclaiming:
Механизм/алгоритм отслеживания неиспользуемых страниц и их переработки (повторного использования) в libmdbx такой-же как в LMDB.
В Сети есть достаточно материалов по этой теме. В том числе презентации Говарда Чу, обзоры/пояснения блогеров и специалистов, где-то была запись моего доклада в 2015 году.

compaction:
Сейчас в MDBX компактификация есть в двух видах:
1) Явная при копировании.
Тут все просто - есть функция (и использующая её утилита), которая обходит b-tree и копирует страницы, а не-используемые просто не копируются.

2) Неявная zero-cost по ходу пишущих транзакций.
Тут тоже не сложно:
- если неиспользуемая/ненужная страница примыкает к концу файла БД, то его можно сделать чуть короче.
- в процессе транзакции libmdbx отслеживает такие страницы и "выталкивает" из основного цикла использования.
- если в конце файла БД образуется достаточно большой зазор, то он усекается.
Это очень простой механизм, его эффективность ограничена, но в целом БД стремиться к уменьшению.
Основной недостаток тут в том, что такая компактификация останавливается, когда в конце файла БД есть используемая страница - до тех пор, пока такая страница не будет обновлена и не попадет в GC.

3) Запланирована реализация явной компактификации, с добавлением в API соответствующей функции.
Алгоритмически эта функция прочитает всю GC в ОЗУ, обойдет все b-tree и затем "насильно" сделает CoW всем страницам близким к концу файла БД - дальше сработает описанная выше неявная zero-cost компактификация.
VS
20:02
Victor Smirnov
Вот, кстати, нет. Я до сих пор не понимаю, как именно (алгоритм) происходит выделение и высвобождение блоков в LMDB/MDBX. Я просмотрел все эти видео и презентации (это одна и та же презентация в разных местах), и там именно про это ничего не сказано. Я так же не могу это понять, читая исходники. А выделение и высвобождение памяти — это ключевой момент дизайна любого метода, основанного на CoW, потому что полностью определяет его конечную производительность. Именно с этим связаны основные патенты в тех же хранилищах данных. Для ZFS/BTRFS информация о том, что они используют подсчет ссылок на блоки зарыта так глубоко, что я смог вытащить её на поверхность в более-менее удобоваримом виде только с помощью ChatGPT. Обсуждают что угодно, только не это. Про LMDB/MDBX СhatGPT начинает практически сразу повторяться, т.е. она про них не знает почти ничего. Про ZFS/OpenZFS/BTRFS знает "нормально", может даже указать имена релевантных функций в исходниках (summarization/elaboration).

Для меня сейчас интересный вопрос, почему LMDB использовал тот метод управления памятью (и он перешел по наследству в MDBX), который он использовал, а не привычный для блочного CoW подсчет ссылок на блоки. Для подсчета ссылок есть красивые алгоритмы, счетчики могут иметь произвольную разрядность, их можно вставлять прямо в bitmap, т.е. они не обязательно будут медленными. В любом случае оно всяко лучше бодания с ID-lists и, самое главное, не было бы проблемы с долгоиграющими читателями.
Л(
20:04
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Покажите ссылку на мою презентацию и видео-запись моего доклада, которые вы смотрели.
Как минимум пойдем по слайдам как по иллюстрациям и я смогу дать пояснения.
AS
20:10
Alex Sharov
In reply to this message
lmdb - не увеличивает и не умешает файл без переоткрытия базы. Это сделано для того чтобы в OS/FS таблицы преобразования виртуальных адресов в физические были минимальны - профит перформансу.
20:14
In reply to this message
«Небылотбы проблемы с читатиоями» - были бы. Нет такой магии где читатель имеет Snapshot Isolation, и одновременно можно нужные ему данные (их страницы или версии) удалить или перезаписать.
Л(
20:16
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Это не так.
LMDB не умеет уменьшать файл, а увеличивает при необходимости одинаково на всех платформах.

На производительность это не влияет, так как имеет значение только при page fault, либо при записи в режиме MDB_WRITEMAP.
При этом page fault примерно в 100-1000 раз более затратен, чем трансляция адреса в номер блока на диске.
В свою очередь, стоимость трансляции в среднем Olog(N) от количества блоков.

Поэтому, в среднем, для не-фиксированного размера БД, LMDB просто всегда медленнее, так как приращение БД выполняется мелкими шагами, что в среднем приводит к более высокой фрагментации.
👍
AS
AS
20:27
Alex Sharov
In reply to this message
Это не так :-)
lmdb не умеет увеличивать файл, а linux по-умному под-капотом наращивает файл автоматом, а на винде сразу весь файл выделяется. Причем Linux даже умеет не создавать файл пока не нужен первый fsync и это дает интересный сайд-эффект lmdb перформит как in-memory база до первого коммита(синка), а mdbx так не умеет (потому что и lmdb не умеет, а linux умеет если в-ручную как mdbx не управлять этим), из-за этого у нас юнит-тесты в 2-3 раза замедлились от перехода на mdbx, но пофиг - сошлись на том что тесты можно в tmpfs.
Л(
20:35
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Алексей, тут какое-то недопонимание, и/или проблемы в юнит-тестах.

Если кратко/упрощенно, в linux каждая страница памяти может быть:
1. не валидна вообще.
2. замеплена анонимно.
3. связана с fd, но не с блоком - тогда при обращении будет page fault и если блок в файле не удастся сопоставить (файл короче), то будет bus error, иначе подкачка содержимого страницы из файла.
4. связана в fd и сопоставлена с блоком в файле.

Если файл не создан (нет fd), а страницы уже есть (с данными в памяти), то "просто так" их в новый файл не замепить.
VS
20:41
Victor Smirnov
In reply to this message
Вот я видел две презентации: раз, два. Давайте я опишу конкретный вопрос, который грызет мне мозг.

В персистентных деревьях для определения того, что узел дерева (в данном случае блок CoW-btree) больше не доступен (версия удалена) используется или подсчет ссылок, или обычный трассирующий GC. С подсчетом ссылок, как только мы удаляем корневой блок версии (снэпшота, коммита,...) идет декремент ссылок на потомков и, если достигли 0, то потомок удаляется. Этот алгоритм имеет трудоемкость O(M), где M — количество блоков, которые достижимы только из удаляемой версии и не достижимы из всех остальных (для последних счетчики не опустятся в 0).

Счетчики можно и не использовать, если у нас "неполноценный" CoW (В LMDB мы храним только две последние версии), но всё равно остается задача: как определить, какие блоки достижимы только из того снэпшота, который вышел из зоны видимости, и не достижимы из "живых" снэшотов. Эти блоки можно переиспользовать. И вот вопрос именно в этом: как, когда и на основе какой информации LMDB/MDBX определяет, что конкретный блок можно переиспользовать. И я тут — как ChatGPT — "примерно понимаю (summary), но вот в детали опуститься не могу".

Скажу сразу, что для меня здесь не является вопросом, как именно организовать атомарное и отказоустойчивое выделение блоков на диске, а так же моменты связанные с "self-referentiality" bitmap/freedb и вытекающих из этого алгоритмических заморочек. Так же не требует разбирательства та особенность дискового CoW, что память, высвобожденная в текущей транзакции может быть использована только в следующей или вообще через одну (зависит от реализации).

#free_space_management
Л(
20:46
Леонид Юрьев (Leonid Yuriev)
@AskAlexSharov, в продолжение моего ответа.

При этом есть отдельный, относительно редкий сценарий:
- создается новая/пустай БД, открывается в режиме не-writemap;
- происходит интенсивное наполнение БД;
- при этом можно не увеличивать размер файла до фактической записи новых страниц.

В этом случае поведение LMDB и MDBX видимо отличается:
- MDBX установит размер файла в соответствии с геометрией и будет его наращивать в соответствии с заданными параметрами, а уже после выделят оттуда страницы и потом из перезаписывать;
- LMDB при этом (видимо, не уверен, но предполагаю) может увеличивать размер файла непосредственно при записи страниц.

Как пояснял выше - так у LMDB в среднем будет больше фрагментация файла БД на диске, но этого не будет заметно если диск больше никем не используется.
Если это поведение MDBX где-то что-то тормозит, то давайте разбираться.
VS
20:49
Victor Smirnov
In reply to this message
В случае подсчета ссылок CoW-btree работала бы точно так же как обычные персистентные структуры данных из функциональных языков программирования за тем лишь исключением, что писатель всё равно может быть только один в каждый момент времени. Именно писатель и удаляет версии, декрементируя ссылки и помещая высвобожденные таким образом блоки в очередь на переиспользование (в следующих транзакциях). Т.е. если читатель "держал" версию, а потом отпустил, блоки, достижимые только из этой удаленной версии, можно определить за O(M), где M — количество блоков (т.е. линейно). Т.е. тут у нас только алгоритмические заморочки, проявляющиеся в высоких значениях скрытых констант оценок трудоемкости и необходимости использовать деревья там, где можно было бы обойтись хэш-таблицой, далай мы все только в RAM.
20:52
In reply to this message
> - LMDB при этом (видимо, не уверен, но предполагаю) может увеличивать размер файла непосредственно при записи страниц.

Да, это основной, и, НСЯЗ, единственный механизм на Linux. Там просто используется разреженный файл, который OS просто оптимизирует так, что его видимый размер может быть сооветствовать количеству реально выделенных страниц. Но даже если сделать запись в конец карты и видимый размер файла станет равен размеру карты, на диске будет всё равно выделено место пропорционально количеству записанных страниц. Это, кстати, еще один потенциальный источник заморочек с IO.
Л(
20:53
Леонид Юрьев (Leonid Yuriev)
Л(
21:24
Леонид Юрьев (Leonid Yuriev)
Суть механизма/алгоритма работы MDBX и сборки мусора:
- пишущие транзакции строго последовательны, нумеруются последовательно, каждая формирует свой MVCC-снимок.
- в начале БД три мета-страницы, в каждой их которых: номер транзакции, признак weak/steady, указатели на корневые страницы двух деревьев (GC/freeDB и "MAIN").
- в MAIN лежат все данные пользователя, а GC/FreeDB используется согласно названию.
- при чтении каждый читатель заглядывает во все мета-страницы и выбирает саму свежую (с наибольшим номером транзакции).

- любое изменение происходит посредством CoW.
- при изменении key-value в листовой странице, создается копия её содержимого (в новой/аллоцируемой странице с другим номером), в эту копию вносятся изменения.
- при клонировании листовой/дочерней страницы, нужно скорректировать ссылку на неё в родительской, которую тоже нужно скопировать, и так до корня дерева.
- таким образом, любое изменение приводит к серии CoW от корня дерева до целевой листовой страницы.

- при фиксации транзакции выбирается самая старая из мета-страниц, при этом заведомо известно, что она не используется читателями.
- фиксируемая транзакция порождает свои версии деревьев GC/FreeDB и MAIN, указатели на которые помещаются в мета-страницы вместе с номером транзакции в самом конце фиксации, после записи данных.

Суть:
- страницы которые были оригиналами для CoW-операций в ходе транзакции, точно не входят в новый MVCC-снимок.
- соответственно эти страницы не нужны читателям, которые будут работать с новым MVCС-снимком (порождаемым фиксируемой транзакцией) и последующими снимками.
- если мы поместим номера этих страниц в GC/FreeDB используя в качестве ключа номер фиксируемой транзакции, то позже сможем переработать (использовать повторно) когда не будет читателей использующих MVCC-снимки ранее формируемого текущей транзакцией.
👍
VS
VS
21:38
Victor Smirnov
In reply to this message
Спасибо, помедитирую над Вашим ответом.
Л(
21:52
Леонид Юрьев (Leonid Yuriev)
In reply to this message
На всякий для понимания - в том числе поэтому я пошел по пути установки размера файла, а не итеративной до-записи при увеличении БД.

Внутри ФС, на самом деле, всё равно может быть отложенное выделение места (и/или отложенная привязка конкретных блоков к именно этому файлу).
Но если так происходит, то значит ФС так сознательно поступает и/или пользователь её так умышленно до-настроил.
VS
22:00
Victor Smirnov
Я думаю, что если хочется вот совсем нормальной (интуитивной и предсказуемой) работы с диском, без всяких этих "навязанных услуг" со стороны FS, то надо класть БД сразу на блочное устройство. LMDB/MDBX это, в отличие от той же RocksDB, "в приципе" умеет (и это то, что делал бы я в проде). Заморочки с FS на самом деле куда большие, чем может показаться. Они кратно возрастают под многопоточной нагрузкой и когда FS приближается к пределу емкости. БД живущие "над" FS — это наследие из эпохи HDD, когда всё упиралось в медленный диск при любом раскладе, и от FS нужна было просто банальная корректность. Сейчас же всё совсем по другому.
22:01
Пытаться же обходить заморочки FS в коде БД — это только усложнять БД, которая и так сложная.
11 March 2023
VS
04:00
Victor Smirnov
AV
04:01
Artem Vorotnikov
MDBX FS

а что, как по мне, это база
VS
04:01
Victor Smirnov
Ну вот, я разобрался. Оставлю это здесь для будущих страдальцев. #free_space_management
04:13
Итак, FreeDB — это отображение TxnID->Set<BlkID>. Картинка выше показывает, как именно происходит высвобождение памяти и её переиспользование, и почему читатели блокируют переиспользование. А так же есть ответ на вопрос, почему счетчики в этой реализации CoW-btree не нужны.

Пусть у нас есть в самом начале транзакция T0 и созданное в ней b+tree. Следующая транзакция T1 модифицирует элемент T1wr. Путь от этого элемента к корню T0 будет склонирован в T1, и соответствующие исходные блоки (показаны кружочками) попадут в FreeDB под ключом T0.

Если теперь мы хотим удалить T0, то ничего делать не надо, блоки, помеченные кружочками можно смело переиспользовать в T2.

T2 модифицирует элемент T2wr (теоретически видимый из T0 и T1) и соответствующий путь, помеченный квадратиками, попадет в FreeDB. На картинке состояние FreeDB для T2.

T2 может смело переиспользовать блоки из T0, если у T0 нет читателя.

Пусть теперь T0 читается, а T1 — "свободна". Мы не можем удалить блоки, помеченные квадратиками, так как они в данным момент видны в читателю T0.
04:15
Т.е. переиспользование блоков может идти только до самого "старого" читателя.
04:21
При выделении новых блоков нам нужно сканировать ту часть FreeDB (это тоже b+tree), для которой Tx < T_oldest_reader. Все эти блоки готовы к переиспользованию. В идеале, FreeDB должна содержать некий синтетический колюч, например TxnID = 0, под который можно компактифицировать все BlkID для старых транзакций, чтобы было легче искать линейные последовательности свободных блоков. Но это не обязательно.
VS
04:47
Victor Smirnov
Теперь, чем эта схема отличается от схемы управления памятью со счетчиками.

В LMDB/MDBX на каждое клонирование блока будет одна запись в FreeDB. Тут можно оптимизировать, буферизуя списки блоков в RAM, сортируя их там же, и записывая потом одной операцией. Т.е. это всё можно делать достаточно быстро, особенно для небольших транзакций. Схема записи, действительно, молниеносная. Недостаток её в том, что (1) читатели должны быть короткими и (2) никаких плюшек в виде savepoints и clones (writable savepoints).

В случае счетчиков, на каждое клонирование branch-узла b+tree будет инкрементироваться порядка 100, в общем-то рандомных, счетчиков. А таких клонируемых блоков будет как минимум 4. Если делать такие счетчики наивно, на основе просто дерева, то производительность будет в районе плинтуса. Если делать счетчики в RAM в быстрой хэш-таблице, то всё получается более-менее сносно. Чемпионом по записи не будет, зато будут все плюшки CoW b+tree, которые в LMDB-шной схеме не сделать (см. выше).

Т.е. для простого OLTP (без плюшек) однозначно лучше схема, примененная в LMDB/MDBX. А вот для аналитики, где запросы могут работать целыми днями, такая схема не подойдет.
VS
06:20
Victor Smirnov
Короче, point в том, что можно иметь три стратегии управления памятью — без счетчиков (для OLTP), со счетчиками в+tree для "петабайтов" и со счетчиками в RAM для "терабайтов". Схему без счетчиков можно тоже использовать для "петабайтов", только без "плюшек" (см. выше) она там не нужна. И теперь у меня есть полный набор стратегий, за что Леониду — огромное спасибо!

За что спасибо? За то, что "поднял" LMDB и разобрался в коде-лапше из goto, и алгоритмах, которые по-хорошему нигде не описаны. Я, действительно, долго не мог понять, как высвобождается память. Не то, что я 10 лет плющил репу каждый день, но раз в год возвращался к проблеме и фейлился.

И вот пруф от, снова, ChatGPT:
06:21
А, собственно, откуда ей знать-то? А интерполирует она хоть и хорошо, но далеко не идеально.
12 March 2023
☭k
18:08
☭ ktrace
@erthink я правильно понимаю, что libmdbx зависимостей не имеет?
18:09
кстати, не планируется reopenldap под cmake загнать?
Л(
19:52
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Да, зависимостей нет.
19:53
In reply to this message
Нет, в этом нет необходимости/потребности.
Там масса других более важных задач.
13 March 2023
07:53
Deleted Account
Hi @erthink

I want to confirm fpta limit:

1) for each row it limit 256KB, 1000 colum
2) no limit for how many rows for one table ?
Л(
08:29
Леонид Юрьев (Leonid Yuriev)
In reply to this message
1) Yes.
2) No explicit limit, but limited by the DB size (upto 2^pages).
08:34
Deleted Account
there is 1024 table limit, so I need more table than 1024? what should I do ?

create multi mdbx instance in one process is possiable ?
Л(
09:12
Леонид Юрьев (Leonid Yuriev)
In reply to this message
The answer is "42".

libfpta is designed for some reasonable scale or range of use cases that we needed.
It work perfect for N<100 and comme-ci comme-ça for N≤1000, but for large N is should be redesigned.
The N here is applicable both for number or column/tuple-fields and number of tables.

Sharding over multiple instances is a bad idea, until you implement your own reliable ACID-enabled synchronization layer over ones.
In the case of multiple tables with the same schema, you can extend the key with a prefix in which to put the ID of the virtualized table.
👍
09:21
Deleted Account
to be comform. the table limit is 42 not 1024.

use prefix as pk for multi similer table, total row numer limit by pages .
Л(
09:31
Леонид Юрьев (Leonid Yuriev)
In reply to this message
The fpta_c_mode in the test subdirectory show actual limits:

$ test/fpta_c_mode
// основные ограничения и константы:
fpta_tables_max = 1024 // максимальное кол-во таблиц
fptu_max_cols = 1022 // максимальное кол-во колонок
fpta_max_indexes = 982 // максимальное кол-во вторичных индексов для одной таблице
fpta_max_dbi = 4096 // максимальное суммарное кол-во таблиц и вторичных индексов
fpta_max_row_bytes = 262140 // максимальная длина строки/записи в байтах
fpta_max_col_bytes = 65531 // максимальная длина значения колонки в байтах
fpta_max_array_len = 2047 // максимальное кол-во элементов в массиве
fpta_name_len_min = 1 // минимальная длина имени
fpta_name_len_max = 64 // максимальная длина имени
fpta_max_keylen = 56 // максимальная длина ключа (дополняется t1ha при превышении)

The comments is in Russian, but it is easy to translate by Google or Yandex.
👍
18:02
Deleted Account
the fpta column name is not storage in database (I need to know if I can change the name late, access by index) ?
18:10
I mean the order of column not changed, but late the string name of column change when init table. Can I still load old data?
Л(
18:54
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Try fpta::schema2json().
For instance, use this unit-test as a example.
19:03
Deleted Account
Thanks.
19:04
There is no fptu_array_* example. and no fptu_int16.

when I try fptu_array_ with fpta_index_none, get this error:

4245 FPTA_ETYPE: Type mismatch
Л(
19:15
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Yes, this feature was not implemented.

Support for arrays has been preprovided/prescribe into design and API for completeness.
But the implementation was postponed until it comes necessary...
14 March 2023
VS
21:01
Victor Smirnov
In reply to this message
Разобрался с этой частью. Да, действительно, у LMDB, в остальном — гениальной, есть серьезнейший косяк дизайна. Транзакции будут просто завершаться с ошибкой, если достаточного размера свободные блоки не будут найдены, а поиск последовательностей свободных блоков — линейный. Тут всё дело в том, что в LMDB нет достаточно эффективного контейнера Mutimap<K, V> == Map<K, Set<V>> и он просто эмулируется через Map<K, PtrToIDList>. В MDBX с включенной BigFoot большие списки бьются на чанки и проблемы выделения блоков нет. Потенциально там нет и других, характерных для CoW с огромными блоками, проблем. Но я туда уже не смотрел, использован ли этот потенциал сейчас или оставлен на будущее.

В общем, резюме такое:

1. Выбор между LMDB и MDBX всегда должен быть в пользу последней, если нет каких-то особых соображений.
2. Знание базовых алгоритмов работы хранилища данных позволяет сознательно избегать "плохих случаев". А их в LMDB, как оказывается, значительно больше, чем интуитивно ожидается.

Леонид, рассматривали ли Вы при реализации BigFoot возможность просто использовать MDBX_DUPSORT для организации списка чанков? С Bigfoot всё нормально, переполнения пространства идентификаторов транзакций можно не опасаться, эффект такой же или даже немного лучше (чанки можно делать упорядоченными). Просто DUPSORT — "из коробки", реализован вполне себе нормально (через sub-database) и это первое, что обычно приходит в голову, когда теоретически нужен Multimap.
Л(
21:58
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Тут несколько иная логика = не все что блестит нужно тянуть внутрь.

Исходно Говард Чу разрабатывал LMDB для LDAP.
В LDAP нет необходимости хранить большие записи в БД (это в целом странно и контр-продуктивно для memory-mapped БД).
Поэтому (насколько я понимаю) изначально вообще не было поддержки длинных записей, Говард добавил это позже самым простейшим способом.
Аналогично в LDAP нет необходимости поддерживать огромные транзакции, в крайнем случае проще однократно компактифицировать БД с помощью утилиты, чем что-то городить внутри БД.
Итого = в LMDB сделано максимально просто, без излишеств, потому-что этого достаточно для целевых сценариев.

В MDBX примерно аналогично = поддержка длинных значений особо ненужна, а огромные транзакции пока только в Erigon.
Важно что это "не нужно" было на момент фиксирования формата БД (где-то в 2018, после реализации динамического размера).
При этом "фишка" BigFoot в том, что он полностью совместим с текущим форматом БД.

Если, "чисто гипотетически", будет некий заказ на разработку (или допеределку) движка БД под какие-то задачи/требования, тогда будет совсем другой заговор.
Например, для длинных данных логично поддержать потоковый формат хранения и io_ring.
А для хранения "списков страниц" в MithrilDB заложен механизм, который (в текущем понимании) ведет себе значительно лучше Roaring Bitmaps.
VS
22:25
Victor Smirnov
In reply to this message
А по MithrilDB есть какая-нибудь открытая информация?
Л(
22:29
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Нет. Точнее только то, что есть в README у libmdbx.
VS
22:30
Victor Smirnov
Кстати, насчет:

> 7. Fast estimation of range query result volume, i.e. how many items can be found between a KEY1 and a KEY2. This is a prerequisite for build and/or optimize query execution plans.

Если запоминать в каждом внутреннем узле дерева количество ключей в поддереве, то можно не оценивать, а знать точно, и так же быстро. Много вкусного/интересного можно будет делать. Например, разбить длинный датасет на равные части и отдать читателям (аналитика). Или получать запись по её номеру (UI, скроллинг).
Л(
22:34
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Да, это очевидное/штатное решение.
Но на момент фиксации формата БД этого не требовалось, а позже было решено прикрутить.
VS
22:35
Victor Smirnov
Это всё понятно, да. Зафиксированный формат БД сложно менять. "Сопротивления" много.
22:38
В MithrilDB для b+tree используются шаблоны C++ или всё как и в MDBX?
Л(
22:46
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Дело не только в этом.
В текущем состоянии код libmdbx (тем более LMDB) очень неудобен для подобных доработок/переделок - много тупой работы, рисков off-by-one и т.п
Рациональнее переписывать на C++ или на Rust, одновременно выкинув все хвосты Windows.
SC
SC
22:47
Simon C.
Rust would be an awesome language for this purpose 🤩
VS
23:04
Victor Smirnov
In reply to this message
Это я всё прекрасно пониманию. Я сам начинал с Си и по итогам "проклял" этот язык. Совершенно не подходит для design space exploration из-за отсутствия мономорфных дженериков. Rust в этом плане, кстати, тоже долго был "не айс" и не знаю, полечили ли там его проблемы к настоящему моменту. В Rust отсутствовали template template, т.е. нельзя было делать стратегии. И нельзя было делать числовые параметры шаблонов. Т.е. толку от них — "почти ноль" для чего-то, более сложного, чем map<K, V> для RAM.

Насчет Roaring Bitmaps. Конкретно для целей аллокации блоков вместо обычных "сжатых" (разреженных) битовых карт лучше использовать rank/select dictionaries. select(n) — это операция, которая возвращает позицию n-го установленного в 1 бита. Делаются они довольно просто, когда есть b+tree. Нужно просто запоминать во внутренних блоках количество установленных в 1 битов в поддереве, вместе с общим количеством битов (точно так же как для случая количества ключей из поста выше).

Для выделения непрерывных последовательностей блоков можно делать "иерархический bitmap": для одного блока, для 2-х, 4-х, 8-и и т.д. Таким образом, как минимум, можно добиться выделения блоков различного, хоть и ограниченного сверху размера. Аналитика, например, "любит" большие блоки, 1MB и более. А проблему больших blobs решать уже путем использования полноценных Multimap. Такой иерархический bitmap довольно просто в реализации и может быть сделан одним b+tree со счетчиками битов, по одному счетчику для каждого уровня.
16 March 2023
Dmitry Ichyotckin invited Dmitry Ichyotckin
Л(
14:14
Леонид Юрьев (Leonid Yuriev)
DI
Dmitry Ichyotckin 16.03.2023 14:11:12
Вопрос делали дорожную карту по предложением? и удобно ли существующий issue для сбора предложений и улучшений?
Л(
14:29
Леонид Юрьев (Leonid Yuriev)
In reply to this message
1. Есть TODO.md в корне проекта, который является "декларацией о намерениях". Другой дорожной карты нет.

2. Большинство существенных пожеланий и улучшений, как правило, связаны с большой работой и/или сменой формата БД.
Формат БД давно зафиксирован, усилия предполагалось направлять на стабилизацию libmdbx, а развитие продолжать в MithrilDB.

3. Но потребности, их приоритеты меняются. Меняется ситуация и т.д.
В ближайшее время я сосредоточен на реализации репликации в новой версии libfpta, а в libmdbx при этом происходят только исправления, либо доработки необходимые для основной активности.
Относительно MithrilDB сейчас есть существенная доля неопределенности, см "MithrilDB and Future" в readme.

4. Для обратной связи по вопросам, пожеланиям, сообщениям о проблемах и т.п. пока достаточно этой группы, issues в gitflic и почты.

5. В целом, разработка libmdbx (и тем более) MithrilDB финансируется Positive Technologies, но libmdbx при этом остается открытым проектом, а я по-возможности стараюсь поддерживать проект в адекватном состоянии.
jsvisa invited jsvisa
17 March 2023
10:39
Deleted Account
today I get this error: MDBX_MAP_FULL

how do I incrase this and get the current value ?
Л(
10:41
Леонид Юрьев (Leonid Yuriev)
In reply to this message
10:42
Deleted Account
thanks. how can I know the current value so I can adjust the new value ?
Л(
10:42
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Please RTFM the above.
10:47
In reply to this message
Opps, I found a cut&paste typo in the doc.
See mdbx_env_info_ex()
👍
11:28
Deleted Account
"mi_mapsize": "1048576",
Л(
11:28
Леонид Юрьев (Leonid Yuriev)
In reply to this message
see mi_geo
12:45
Deleted Account
{
"mi_geo": {
"lower": "12288",
"upper": "1048576",
"current": "589824",
"shrink": "131072",
"grow": "65536"
},
"mi_mapsize": "268435456",
"mi_last_pgno": "136",
"mi_recent_txnid": "1199342",
"mi_latter_reader_txnid": "1199342",
"mi_self_latter_reader_txnid": "1199342",
"mi_meta0_txnid": "1199341",
"mi_meta0_sign": "18446744073709551615",
"mi_meta1_txnid": "1199342",
"mi_meta1_sign": "1",
"mi_meta2_txnid": "1199340",
"mi_meta2_sign": "18446744073709551615",
"mi_maxreaders": "242",
"mi_numreaders": "13",
"mi_dxb_pagesize": "4096",
"mi_sys_pagesize": "4096",
"mi_bootid": {
"current": {
"x": "12795452472403286092",
"y": "3556082723772320340"
},
"meta0": {
"x": "12795452472403286092",
"y": "3556082723772320340"
},
"meta1": {
"x": "12795452472403286092",
"y": "3556082723772320340"
},
"meta2": {
"x": "12795452472403286092",
"y": "3556082723772320340"
}
},
"mi_unsync_volume": "20480",
"mi_autosync_threshold": "0",
"mi_since_sync_seconds16dot16": "159574",
"mi_autosync_period_seconds16dot16": "0",
"mi_since_reader_check_seconds16dot16": "2260288",
"mi_mode": "67960832",
"mi_pgop_stat": {
"newly": "11",
"cow": "33",
"clone": "0",
"split": "0",
"merge": "0",
"spill": "0",
"unspill": "0",
"wops": "1",
"prefault": "0",
"mincore": "0",
"msync": "33",
"fsync": "0"
}
}
12:48
after crease mi_mapsize, the mi_geo.upper still is 1048576.
13:10
Deleted Account
errno = mdbx_env_set_mapsize(m_env, 268435456);
errno = mdbx_env_set_geometry(m_env, -1, -1, 268435456, 65536, 131072, -1);

I call this, both call return 0.

but the mi_geo remain unchanged.
13:51
Deleted Account
delete database, use log rebuild it. and set it work for me now.
20 March 2023
11:00
Deleted Account
run MDBX_SAFE_NOSYNC | MDBX_WRITEMAP insde container.

If container crashed, will made last commit not saved ?
Л(
12:23
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Container is an abstraction, so the answer depends on each particular case.

In general, unflushed-to-disk data is inside a file system cache, or actually in a unified page/buffer cache on most systems.
Thus, if such cache "lives" inside a container, then you will lose you data.
Otherwise, if such cache "lives" inside a host system, then all will be OK.
12:27
Deleted Account
it is the linux podman container, is it insde container or host ?
Л(
12:34
Леонид Юрьев (Leonid Yuriev)
In reply to this message
I would prefer to understand the reasons why you did not succeed initially.

Presumably, the reason is that you were calling mdbx_env_info() within a read-only transaction started before the mdbx_env_set_geometry() was called.
In this case you didn't see geometry changes due Isolation in the ACID.
👍
12:37
In reply to this message
You should known this but not ask here ;)
12:41
Deleted Account
I know the cache file is mount from host into container. this this mean the cache file (dir own the files) in the host ?
21 March 2023
16:21
Deleted Account
MDBX_WANNA_RECOVERY: Database should be recovered, but this could NOT be done automatically for now since it opened in read-only mode
16:21
I open it as read write mode, but get this error.
Л(
16:21
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Just run mdbx_chk -wvvv path-to-db
👌
16:26
Deleted Account
./mdbx_chk -wvvv /root/.gcache
mdbx_chk v0.12.4-0-g53177e48 (2023-03-03T23:23:08+03:00, T-22fb6858ad8f8370397fd821d01b2e51d73b7e14)
Running for /root/.gcache in 'read-write' mode...
~ opening after an unclean shutdown, but boot-id(b192943afdfdd84c-3159be65765a4a54) is MATCH: rollback NOT needed, steady-sync NEEDED
open-MADV_DONTNEED 350..352
readahead ON 0..350
- monopolistic mode
fallback to pthreads' tsd, key 0, count 0
- current boot-id b192943afdfdd84c-3159be65765a4a54
- pagesize 4096 (4096 system), max keysize 1980..2022, max readers 114
- mapsize 1073741824 (1.00 Gb)
- dynamic datafile: 12288 (12.00 Kb) .. 1073741824 (1.00 Gb), +65536 (64.00 Kb), -131072 (128.00 Kb)
- current datafile: 1441792 (1.38 Mb), 352 pages
- meta-0: steady txn#91660, tail
- meta-1: steady txn#91661, stay
- meta-2: weak-intact (same boot-id) txn#91662, head
- performs check for meta-pages clashes
- performs full check recent-txn-id with meta-pages
! steady meta-1 txn-id mismatch recent-txn-id (91661 != 91662)
- transactions: recent 91662, latter reader 91662, lag 0
Traversal b-tree by txn#91662...
- pages: walked 343, left/unused 7
@GC: subtotal 1, leaf 1
- usage: total 1404928 bytes, payload 821372 (58.5%), unused 583556 (41.5%)
@GC: subtotal 4096 bytes (0.3%), payload 92 (2.2%), unused 4004 (97.8%)
- summary: average fill 58.5%, 0 problems
Processing @GC...
- key-value kind: ordinal-key => single-value, flags: integerkey (0x08), dbi-id 0
- last modification txn#91662
- page size 4096, entries 2
- b-tree depth 1, pages: branch 0, leaf 1, overflow 0
- fixed key-size 8
- summary: 2 records, 0 dups, 16 key's bytes, 36 data's bytes, 0 problems
- space: 262144 total pages, backed 352 (0.1%), allocated 350 (0.1%), remained 261794 (99.9%), used 343 (0.1%), gc 7 (0.0%), detained 4 (0.0%), reclaimable 3 (0.0%), available 261797 (99.9%)
Processing @MAIN...
- key-value kind: ordinal-key => single-value, flags: integerkey (0x08), dbi-id 1
- last modification txn#91662
- page size 4096, entries 23758
- b-tree depth 3, pages: branch 3, leaf 336, overflow 0
! corrupted branch-page #324, mod-txnid 91662
! node[69] key wrong order (<0100000011300100> >= <1900000043500000>)
! node[75] key wrong order (<0100000011d00700> >= <0100000014b00200>)
! node[76] key wrong order (<0100000014b00200> >= <0100000033300100>)
! mdbx_cursor_get() failed, error -30796 MDBX_CORRUPTED: Database is corrupted
entry #0: different number of entries (0 != 23758)
- problems: different number of entries (1)
- summary: 0 records, 0 dups, 0 key's bytes, 0 data's bytes, 1 problems
Skip processing sub-database(s) since @MAIN is corrupted (4294967295 problems)
Total 34 errors are detected, elapsed 0.004 seconds.
16:27
still can not open
16:28
I has remove MDBX_WRITEMAP, since it keep give wrong result for key (compare to logs, a key give a old value, but new value already updated). now just MDBX_SAFE_NOSYNC in singe thread write.
Л(
16:29
Леонид Юрьев (Leonid Yuriev)
In reply to this message
are you using custom comparators ?
16:30
Deleted Account
you mean this : MDBX_NOSUBDIR | MDBX_COALESCE | MDBX_LIFORECLAIM | MDBX_SAFE_NOSYNC ?

I run it in musl linux container
16:32
I am not sure which part about : comparators
Л(
16:32
Леонид Юрьев (Leonid Yuriev)
I mean custom functions for key comparison.
16:33
Deleted Account
NO, not set this kind argumets.
16:35
After I move the file into other folder:

/root/.gcache from 125 error 2: No such file or directory
init errno 2 from 126 with /root/.gcache
app can not start (recreate from old location, but there is no file in /root, still not able to open)
16:48
is there a way to start it without reboot full system ? I restart container but not work
Л(
16:49
Леонид Юрьев (Leonid Yuriev)
1. Judging by the diagnostics, your database is damaged, but very strange.
Some entries has wrong order (like a custom key-comparison function was used), but page has valid mod-txnid.
For now ,I have no idea how this could be happen, even if container lost some page-writes.
Please run mdbx_chk -ivvv to check DB ignoring key-ordering.

2. Please do not some other, and not restart system until we solve this issue.
👌
16:51
Deleted Account
mdbx_chk -ivvv ./.gcache
mdbx_chk v0.12.4-0-g53177e48 (2023-03-03T23:23:08+03:00, T-22fb6858ad8f8370397fd821d01b2e51d73b7e14)
Running for ./.gcache in 'read-only' mode...
! opening after an unclean shutdown, but boot-id(b192943afdfdd84c-3159be65765a4a54) is MATCH: rollback NOT needed, steady-sync NEEDED, but unable in read-only mode
91660.s:91661.s:91662.w, fsm=0x03, head=2-91662.w, base=1-91661.s, tail=0-91660.s, valid Y, strict Y
! mdbx_env_open() failed, error -30419 MDBX_WANNA_RECOVERY: Database should be recovered, but this could NOT be done automatically for now since it opened in read-only mode
Please run mdbx_chk in the read-write mode (with '-w' option).
Л(
16:52
Леонид Юрьев (Leonid Yuriev)
Oh, please mdbx_chk -iwvvv
16:53
Deleted Account
mdbx_chk -iwvvv ./.gcache
mdbx_chk v0.12.4-0-g53177e48 (2023-03-03T23:23:08+03:00, T-22fb6858ad8f8370397fd821d01b2e51d73b7e14)
Running for ./.gcache in 'read-write' mode...
~ opening after an unclean shutdown, but boot-id(b192943afdfdd84c-3159be65765a4a54) is MATCH: rollback NOT needed, steady-sync NEEDED
open-MADV_DONTNEED 350..352
readahead ON 0..350
- monopolistic mode
fallback to pthreads' tsd, key 0, count 0
- current boot-id b192943afdfdd84c-3159be65765a4a54
- pagesize 4096 (4096 system), max keysize 1980..2022, max readers 114
- mapsize 1073741824 (1.00 Gb)
- dynamic datafile: 12288 (12.00 Kb) .. 1073741824 (1.00 Gb), +65536 (64.00 Kb), -131072 (128.00 Kb)
- current datafile: 1441792 (1.38 Mb), 352 pages
- meta-0: steady txn#91660, tail
- meta-1: steady txn#91661, stay
- meta-2: weak-intact (same boot-id) txn#91662, head
- performs check for meta-pages clashes
- performs full check recent-txn-id with meta-pages
! steady meta-1 txn-id mismatch recent-txn-id (91661 != 91662)
- transactions: recent 91662, latter reader 91662, lag 0
Traversal b-tree by txn#91662...
- pages: walked 343, left/unused 7
@GC: subtotal 1, leaf 1
- usage: total 1404928 bytes, payload 821372 (58.5%), unused 583556 (41.5%)
@GC: subtotal 4096 bytes (0.3%), payload 92 (2.2%), unused 4004 (97.8%)
- summary: average fill 58.5%, 0 problems
Processing @GC...
- key-value kind: ordinal-key => single-value, flags: integerkey (0x08), dbi-id 0
- last modification txn#91662
- page size 4096, entries 2
- b-tree depth 1, pages: branch 0, leaf 1, overflow 0
- fixed key-size 8
- summary: 2 records, 0 dups, 16 key's bytes, 36 data's bytes, 0 problems
- space: 262144 total pages, backed 352 (0.1%), allocated 350 (0.1%), remained 261794 (99.9%), used 343 (0.1%), gc 7 (0.0%), detained 4 (0.0%), reclaimable 3 (0.0%), available 261797 (99.9%)
Processing @MAIN...
- key-value kind: ordinal-key => single-value, flags: integerkey (0x08), dbi-id 1
- last modification txn#91662
- page size 4096, entries 23758
- b-tree depth 3, pages: branch 3, leaf 336, overflow 0
- fixed key-size 8
- summary: 23758 records, 0 dups, 190064 key's bytes, 380112 data's bytes, 0 problems
Scanning @MAIN for sub-database(s)...
- does not contain multiple databases
Perform sync-to-disk for make steady checkpoint at txn-id #91662
No error is detected, elapsed 0.006 seconds
Л(
16:54
Леонид Юрьев (Leonid Yuriev)
OK.
Please show me the output of git grep mdbx_dbi_open for your code.
Л(
16:58
Леонид Юрьев (Leonid Yuriev)
In reply to this message
No.
This is for libmdbx's code (I don't see any other),
but please for your (i.e. you own) code.
17:00
Are you sure not using the mdbx_dbi_open_ex() ?
17:01
Deleted Account
flags = MDBX_INTEGERKEY;
if( !readonly ) {
flags = flags | MDBX_CREATE;
}
errno = mdbx_dbi_open(txn, null, flags, &m_dbi);
Л(
17:03
Леонид Юрьев (Leonid Yuriev)
Hmm..., OK.
I will test (i.e. add one more test case).
17:05
Please make a backup (to sure I could investigate more) and then hope you can continue.
17:08
Deleted Account
OK, just tar cvf will be OK ?
Л(
17:08
Леонид Юрьев (Leonid Yuriev)
In reply to this message
yes.
17:09
Deleted Account
OK. but the other problem is. I can not open it. with create option.
Л(
17:11
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Please upload/send the created "backup" tarball, I will dig.
// this could be done via private message or by email.
17:12
Deleted Account
OK
23 March 2023
09:49
Deleted Account
in a multi process case, if one process restarted multi times, the mi_numreaders will keep grow ?

In my case I run 4 go app, run with GOMAXPROCS=8, 8, 16, 32. so total shoul be 64 thread. and there is also one app with 15 thread for writer and manage.

I find the mi_numreaders incrased to 242, I am not sure why. (I guess some go app restart cause this)
AS
10:29
Alex Sharov
In reply to this message
GOMAXPROCS - it's amount of threads to use for runninng goroutines. not total threads limit.
you still can have unlimited amount of threads which waiting for IO (page-fault in mdbx case)
10:30
Deleted Account
So there is extra 140 thread not run coroutine?
AS
10:35
Alex Sharov
In reply to this message
Imagine you start 1K goroutines with GOMAXPROC=4:
- 4 threads are running unlimited amount of goroutines which are not blocked by IO
- if some goroutine is blocked by IO, it locked to current thread and this thread is removed from GOMAXPROC's thread-pool, and waiting for IO to finish. When IO-wait is done - such gorooutine can continue work on GOMAXPROC's thread-pool.

what happening in your case - i can't know. maybe something else happening (maybe you open but don't abort read-transactions).
10:43
Deleted Account
I am not know go very much, but I dont think a block IO need a thread to wait. in that case go will not be able to handle 10K connection.
10:45
and I can count thread by htop -p pid. there is not much thread there.
AS
11:09
Alex Sharov
In reply to this message
"http connections" and "page-fault inside C library" it's not the same thing
👍
Л(
24 March 2023
Л(
01:44
Леонид Юрьев (Leonid Yuriev)
Пожалуйста, кто-нибудь, напишите Максу на адрес maxc0d3r@protonmail.com и поясните, что я не могу ответить ему, так как доступ к Proton заблокирован из России (с ним не могут взаимодействовать почтовые шлюзы с DKIM/SPF/DMARC), а с произвольных IP-адресов (тем более VPN) Proton письма не принимает. Поэтому просьба взаимодействовать через эту группу либо https://gitflic.ru/project/erthink/libmdbx/issue.

Спасибо.

- - -

Please, someone, write to Max at maxc0d3r@protonmail.com and explain that I cannot answer him, since access to Proton is blocked from Russia (mail relays with DKIM/SPF/DMARC cannot connect to), and Proton does not accept mail from arbitrary IP addresses (especially VPN). Therefore, please interact through this group either https://gitflic.ru/project/erthink/libmdbx/issue .

Thanks.
👍
A
31 March 2023
Dmitriy Golondarev invited Dmitriy Golondarev
Alexander Brilliantov invited Qjawko
3 April 2023
Jack invited Jack
5 April 2023
СМ
08:59
Сергей Мирянов
@erthink Леонид, добрый день!
у меня возникла необходимость удалять файлы после закрытия (работать с БД во временном файле)
я знаю про O_TMPFILE и FILE_DELETE_ON_CLOSE
если внести эти изменения - будут ли какие-то проблемы в работе libmdbx? нет ли каких-то ограничений?
Л(
09:17
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Приветствую.

Неустранимых технических проблем быть не должно, но:
- В зависимости от режима работы используется несколько хендлов, вам нужно будет выбирать NOSYNC.
- O_TMPFILE фактически удаляет файл после создания, т.е. счетчик ссылок (st_nlink в информации от fstat()) становится нулевым, а в коде библиотеки есть проверки и ветвления. Вам придется вникнуть в логику их испрльзования и отключить.
- В Linux вы сможете работать с БД только из одного процесса;
- Как поведет себя Windows не знаю, теоретически либо не даст открыть второму процессу, либо будет пытаться удалять файлы при каждом закрытии.

В целом нужно пробовать.
Однако, это достаточно нелогичный режим работы БД, поэтому его поддержки нет и не будет в libmdbx.
Возможно что проще использовать какой-то адаптер над std::map<>.
СМ
09:21
Сергей Мирянов
ясно, спасибо за подробный ответ.

то что работать можно будет только из одного процесса не пугает - в данном случае так оно и будет.
пугает больше "в коде библиотеки есть проверки и ветвления" - я надеялся что только открытие файла (и связанный с ним code path) придется поправить.

буду думать, возможно ваше предложение с std::map действительно лучший вариант. спасибо!
8 April 2023
Melbourne Channel Moderator invited Melbourne Channel Moderator
MM
20:37
Melbourne Channel Moderator
> Allows a swarm of multi-threaded processes to ACIDly read and update several key-value maps and multimaps in a locally-shared database.

does this means more than one write transaction can be used in multiple processes to access this mdbx?
СМ
20:49
Сергей Мирянов
There cannot be more than one writer at a time, i.e. no more than one write transaction at a time. But you can write from multiple processes - one txn after one.
👍
VS
VS
21:18
Victor Smirnov
Just to clarify possible misunderstanding. Many processes can write into to the same DB, but only one at a time. There is a shared lock for that.
👍
СМ
Л(
9 April 2023
MM
00:51
Melbourne Channel Moderator
In reply to this message
means u have to close and fsync the db if u want to write from another process?
00:51
In reply to this message
i see. so ... means it's now possible to do write transactions without closing db?
👍
СМ
VS
01:00
Victor Smirnov
In reply to this message
Sure. All processes work with the shared DB file as usual, except that there is only one write Txn at time. All concurrent workers will be waiting for active Txn to either commit or abort (not sure what exactly happens if active writer crashes).
10 April 2023
MM
09:35
Melbourne Channel Moderator
In reply to this message
Good question. What happens etc.
Wheres the official non github repo now? I didnt touch mdbx for 1 year and seemed this feature is new
09:36
Came back to mdbx coz looking for a multi writer concurrent db.
Л(
09:46
Леонид Юрьев (Leonid Yuriev)
In reply to this message
> All concurrent workers will be waiting for active Txn to either commit or abort (not sure what exactly happens if active writer crashes).

libmdbx provide ACID even if a writer (the process running a write transaction) crashed.

However, there is a non-zero probability of a process with abnormal behavior could corrupt DB by write to a particular file descriptor or by write-dereference an invalid pointer in MDBX_WRITEMAP mode.
Also a DB could be corrupted in case of system crash (power failure, kernel oops , etc) in MDBX_UTTERLY_NOSYNC mode.

Please RTFM for more details.
09:49
In reply to this message
Please read the last line of https://github.com/erthink/libmdbx/blob/master/README.md or look at the last commit that changed README.md file.
MM
09:51
Melbourne Channel Moderator
In reply to this message
Actually u shld put it in the first line.
Л(
09:52
Леонид Юрьев (Leonid Yuriev)
MM
12 April 2023
Л(
20:59
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Seems a SPAM
13 April 2023
🧶 quiet 🫧 invited 🧶 quiet 🫧
10:22
Deleted Account
I try open MDBX_EXCLUSIVE | MDBX_NOSUBDIR | MDBX_COALESCE | MDBX_SYNC_DURABLE from android, get this error:

MDBX_INCOMPATIBLE: Environment or database is not compatible with the requested operation or the specified flags
10:24
any suggestion how to make this work ?
10:38
I try adjust the flags, all return same error.

The app work for samsung OS, but not vivo.
Л(
14:33
Леонид Юрьев (Leonid Yuriev)
In reply to this message
The current version of libmdbx requires a linux kernel >= 4.0 and lock-free atomic operations for 32/64 bit words.
Perhaps you will see the reason if enable logging by mdbx_setup_debug().
Otherwise your should step-by-step trace down in debugger until reach the return MDBX_INCOMPATIBLE.
👍
14 April 2023
08:01
Deleted Account
There is a lot android kernel old then 4.0. I has to find other solution for all mobile device.
👍
SC
SC
08:02
Simon C.
In reply to this message
You can actually disable that check in the mdbx source at your own risk. Works quite well for me
👍
VS
08:19
Deleted Account
I am not sure this can work for us. the android device cloud have very old kernel. like 3.0(Jelly Bean), 3.1(Kit Kat).

@erthink will it safe for us to disable the kernel version check ?
SC
09:22
Simon C.
In reply to this message
Yes that’s probably too old. I wouldn’t do it below Lolipop
Л(
11:25
Леонид Юрьев (Leonid Yuriev)
In reply to this message
In general - no, it is not safe.

There are some bugs in older kernels that need workarounds, which were removed from libmdbx last year.
For instance, the fdatasync() syscall don't sync metadata of a file, so a DB and filesystem could be corrupted on system failure.
The more general problem is that testing on older kernels has not been done for a long time. And on very old ones it was never performed at all.

It would be nice if someone took up such testing. For example, he developed a set of cases and scripts for testing using hypervisors and/or qemu.
👍
17 April 2023
vad babushkin invited vad babushkin
Л(
11:49
Леонид Юрьев (Leonid Yuriev)
Call for testing

Seems the master branch is ready for 0.12.5
Please check it out.

https://gitflic.ru/project/erthink/libmdbx
👍
СМ
YS
A
5
🔥
1
18 April 2023
Л(
12:04
Леонид Юрьев (Leonid Yuriev)
v0.12.5 "Динамо" от 2023-04-18

Стабилизирующий выпуск с исправлением обнаруженных ошибок и устранением недочетов, в день 100-летнего юбилея спортивного общества «Динамо».
16 files changed, 686 insertions(+), 247 deletions(-)

Благодарности:
- Max <maxc0d3r@protonmail.com> за сообщение о проблеме экспорта из DSO/DLL устаревших функций API.
- @calvin3721 за сообщение о проблеме работыMainDB с флагами не по-умолчанию.

Исправления:
- Поправлен экспорт из DSO/DLL устаревших функций,которые заменены на inline в текущем API.
- Устранено использование неверного компаратора при создании или пересоздании MainDB с флагами/опциями предполагающим использование специфического компаратора (не по-умолчанию).

Мелочи:
- Удалена дублирующая диагностика внутри node_read_bigdata().
- Исправлены ссылки в описании mdbx_env_set_geometry().
- Добавлен отдельный тест extra/upsert_alldups для специфического сценария замены/перезаписи одним значением всех multi-значений соответствующих ключу, т.е. замена всех «дубликатов» одним значением.
- В C++ API добавлены варианты buffer::key_from() с явным именованием по типу данных.
- Добавлен отдельный тест extra/maindb_ordinal для специфического сценария создания MainDB с флагами требующими использования компаратора не по-умолчанию.
- Рефакторинг проверки "когерентности" мета-страниц.
- Корректировка osal_vasprintf() для устранения предупреждений статических анализаторов.

https://gitflic.ru/project/erthink/libmdbx/release/ed56983c-df98-434a-89b8-1b3cd50c1248
👍
SC
СМ
6
23 April 2023
Nodir Temirkhodjaev invited Nodir Temirkhodjaev
25 April 2023
07:45
Deleted Account
In reply to this message
Fantastic, i see a nice performance improvement on Windows for write operations with 0.12.5 👍

However, when i open the env with standard settings i frequently get MDBX_MAP_FULL -30792 on Windows when rapidly filling the db running specs and benchmarks.

I think this is a bug, but i am not sure, maybe i am supposed to change settings for my usecase?

Currently i am using:

mdbx_env_set_geometry(env, -1, -1, MAX_VALUE_SIGNED(size_t), 4194304, 4194304, MDBX_MAX_PAGESIZE);

and problem goes away.
👍
Л(
Л(
10:25
Леонид Юрьев (Leonid Yuriev)
In reply to this message
By default (without calling mdbx_env_set_geometry()) the size of a new DB is 2 megabytes.
Such value is inherited from LMDB and keeps for compatibility.
So a bug is in case if MDBX_MAP_FULL occurs when the database cannot yet be filled up to the specified size.
10:35
Deleted Account
In reply to this message
I see. So its not a bug, i have to set size_upper. Thanks for clarifying.
26 April 2023
Dmitriy Zaviyalov invited Dmitriy Zaviyalov
Л(
11:53
Леонид Юрьев (Leonid Yuriev)
Сегодня вышел GCC 13.1, в котором добавлена генерация предупреждений при использовании int и unsigned вместо enum-типов.
Исторически в libmdbx было именно такое, поэтому сборка выпуска v0.12.5 может ломаться (если используется -Werror).
Соответствующие изменения были внесены в ветку master неделю назад.
SC
27 April 2023
A
17:28
Aleksei🐈
Леонид, а возможно опциями не создавать lck файл, если не используется межпроцессный доступ и вообще вся логика в одном потоке? Или lck файл обязателен в любом случае?
Л(
17:46
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Сейчас обойтись без LCK-файла можно только при расположении БД на файловой системе только для чтения, и соответственно при использовании БД только для чтения.
Для любых других вариантов необходимо существенно дорабатывать логику работы БД, в том числе переделывать реализацию блокировок.
A
18:50
Aleksei🐈
In reply to this message
Понял, спасибо
28 April 2023
Л(
00:13
Леонид Юрьев (Leonid Yuriev)
Благодаря Россотрудничеству разработчики и пользователи из дружественных стран получают приоритетную поддержку, в особенности из: Китая, Ирана, Северной Кореи, Кубы, Венесуэлы и др.
Детали программы будут уточняться, вплоть до предоставления консультаций и аудите исходного кода.

В текущем понимании/редакции условия такие:
- запрос должен быть оформлен через https://gitflic.ru/project/erthink/libmdbx/issue
- запрос должен быть сформулирован на русском, белорусском или национальном (строго не-английском) языке.
- запрос не должен быть бесполезным, абсурдным, содержать оскорбления или еще какую-то неподобающую ерунду.
🔥
VP
A
6
🤔
1
29 April 2023
Л(
22:29
Леонид Юрьев (Leonid Yuriev)
v0.12.6 "ЦСКА" от 2023-04-29

Стабилизирующий выпуск с исправлением обнаруженных ошибок и устранением недочетов, в день 100-летнего юбилея спортивного клуба «ЦСКА».
14 files changed, 117 insertions(+), 83 deletions(-)

Доработки:
- Обновление патча для старых версий buildroot.
- Использование clang-format-16.
- Использование enum-типов вместо int для устранения предупреждений GCC 13, что могло ломать сборку в Fedora 38.

https://gitflic.ru/project/erthink/libmdbx/release/b186e50f-de32-4748-a730-d1ebf54a7aa7
👍
VS
СМ
SC
2
1 May 2023
Л(
18:07
Леонид Юрьев (Leonid Yuriev)
Следующий релиз 0.13.1 буден назван в честь ЧВК «Вагнер», которая продолжает набор добровольцев по всей России!
🤙🏻
🔥
СМ
AV
SC
18:09
Simon C.
In reply to this message
Don't you think it would be good to keep this channel and project politically neutral?
👍
i
4
AV
18:16
Artem Vorotnikov
In reply to this message
Somehow blue-yellow flags do get a pass on this matter
SC
18:17
Simon C.
In reply to this message
Russian flags do too. But I'd like to see complete neutrality 😉️️️️️️
AV
18:18
Artem Vorotnikov
Besides, mdbx and Leonid got banned from GitHub anyway, might as well show the finger 😉
SC
18:18
Simon C.
Sure it's completely up to him. Just me asking nicely not to answer mistakes with the same mistakes 🙂
Александр invited Александр
Εγο invited Εγο
Zlobny invited Zlobny
Deleted invited Deleted Account
Л(
19:02
Леонид Юрьев (Leonid Yuriev)
In reply to this message
В целом, я уважаю позицию всех участников, в том числе тех, кто хочет и/или предпочитает оставаться в стороне от конфликта.
Поэтому тут не будет какой-либо пропаганды, но будет минимальная информация выражающая мою позицию:

(1) Слишком много лжи почти во всех СМИ (если не считать российские, белорусские и китайские). Причем это многослойная и давняя ложь, не только о происходящем, но и о прошедших исторических событиях и давно завершившихся процессах. Мы не можем продолжать игнорировать эту ложь по массе причин.
По тем-же причинам мы вынуждены давать отпор с полной решимостью, у нас просто нет выбора.

(2) У людей разный уровень информированности, вплоть до полного непонимания причин и происходящих процессов. При этом многие почему-то уверены, что получают достаточно объективной (а не ложной или искаженной) информации, чтобы формировать и отстаивать свою позицию. С одной стороны, это их право, в том числе в выборе источников информации, в доверии к ним, даже право быть обманутыми. С другой стороны, действие предполагает ответственность, в том числе и бездействие в форме молчаливого согласия. Поэтому декларируя свою позицию и готовность её отстаивать, я хочу стимулировать всех, даже тех кто хочет остаться в стороне, искать альтернативные источники информации и т. д.

(3) О своей информированности я относительно спокоен. Офицеры НАТО не только теряют документы в туалетах, но и сообщают подробности некоторых гнусностей/подлостей исходя из принципов морали и общегуманистических ценностей.

Ну и в ЧВК «Вагнер» я пока не вступил по простой причине: необходимо выполнить обещания/обязательства и завершить некоторые дела.
🤙🏻

- - -

В сухом остатке:

- Эта группу будет оставаться максимально политически нейтральной, но я (крайне редко) буду позволять себе доносить информацию о собственной позиции.

- Предположительно, это позволит как избежать offtopic-обсуждений, так и спама по около-политическим вопросом (сегодня как-раз такое было).
👍
Y
M
MK
11
SC
19:06
Simon C.
I'll not respond to 1, 2 and 3 as I don't think it's the right forum or bring benefit here. It's also hard for me. I appreciate the effort of everyone in this channel to stay neutral.
19:15
Deleted Account
In reply to this message
Thanks for your clarification. Also please consider that the sanction laws some of the users and contributors of libmdbx have to obey, are of very bad quality and leave a lot of room for interpreatation, possibly incriminating the users or contributors just by integrating libmdbx sources or otherwise.
👍
VS
AV
19:18
Artem Vorotnikov
In reply to this message
I am not Leonid, but IMHO naming of releases is rather irrelevant here - compared to connection to Positive Technologies, implied and used as casus belli by GitHub
19:21
Deleted Account
In reply to this message
Sure, I mean in general. Its a complicated situation.
живчик invited живчик
Евгений Кортов invited Евгений Кортов
Л(
19:24
Леонид Юрьев (Leonid Yuriev)
In reply to this message
The quote from the README from the end of "MithrilDB and Future" section:

Contrary to MithrilDB, libmdbx will forever free and open source. Moreover with high-quality support whenever possible. Tu deviens responsable pour toujours de ce que tu as apprivois. So we will continue to comply with the original open license and the principles of constructive cooperation, in spite of outright Github sabotage and sanctions. I will also try to keep (not drop) Windows support, despite it is an unused obsolete technology for us.
LP
YS
7
Deleted invited Deleted Account
19:37
Deleted Account
In reply to this message
Братан, ты лучший! С тобой вся здравая Россия!❤️
LP
ж
Роман invited Роман
Андрей Щ invited Андрей Щ
CHE_Romanov_Matvey invited CHE_Romanov_Matvey
Л(
20:34
Леонид Юрьев (Leonid Yuriev)
Камрады, есть задача для фрилансера или волонтера/контрибьютора, технического писателя или может-быть студента:
Нужно сделать хороший пример использования C++ API и написать статью (желательно сразу на двух языках, medium + habr, etc).

При хорошем качественном результате я могу пообещать оплату, но это не должно быть основным стимулом.
Скорее это хороший повод разобраться в теме, расширить кругозор, прокачать навыки, попробовать силы и т.п..
Начинающих специалистов может заинтересовать возможность устроиться на работу или получить рекомендации.

Если делать достойно, а именно так и требуется, то задача совсем не простая и (предположительно) потянет на курсовую.
Потребуется знание актуального C++, понимание принципов работы БД, lock-free/wait-free, концептов key-value и части внутренней механики MDBX/LMDB.
Сам я этим занимать как не могу из-за отсутствия времени, так и не хочу, ибо нужен свежий взгляд человека не погруженного в libmdbx.

Концепция примерно такая:
1) Знакомство с ключевыми возможностями и основными недостатками libmdbx.
2) Подбирается задача создания простого хранилища на основе libmdbx.
Например, для условного минималистского мессенджера или примитивного блокчейна, либо какая-то искусственная/умозрительная задача. Главное чтобы по ходу статьи сделать 2-3 шага от простого к сложному.
3) Знакомство с C++ API, включая необходимое количество серий ответов/вопросов.
4) Несколько циклов написание кода и текста, с рецензированием и обсуждением, до взаимного удовлетворения результатом.
5) Читателю должно быть интересно, а в конце и понятно как сделать что-то полезное на С++ API для libmdbx.

Заинтересованных прошу выходить на контакт при наличии 2-3 вариантов для второго пункта.

@k0tb9g9m0t, просьба обратить внимание - весьма вероятно что среди вашей аудитории будут заинтересованные.
AB
20:43
Aleksandr Borgardt
В полное возможно
👍
Л(
Yuriy invited Yuriy
AV
21:10
Artem Vorotnikov
In reply to this message
Для свежеприбывших отмечу, что у libmdbx есть не только C и C++ API.

Есть биндинги к большому количеству языков, в т.ч. безопасный API с ORM для Rust:
https://docs.rs/libmdbx
СМ
21:10
Сергей Мирянов
In reply to this message
У нас примерно так, кроме использования с++ api :)
михаил invited михаил
2 May 2023
Mark invited Mark
Alexander M invited Alexander M
Л(
15:45
Леонид Юрьев (Leonid Yuriev)
Если у кого-то есть аккаунт на WP или AppleID, при желании можете отписать комментарий Malte Skarupke (aka probably-dance), о том что есть чуть более быстрая реализация branch-less bsearch/lower_bound, в том числе с workaround для clang.
https://gitflic.ru/project/erthink/bsearch-try
👍
СМ
SC
SC
15:47
Simon C.
In reply to this message
I feel like the translation of this message is slighly incorrect, not sure what you mean
Л(
15:55
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Ok.

If someone (with an account on WP or Apple ID) wish, then could submit a comment to Malte Skarupke's acticle that there is a slightly faster implementation of branch-less bsearch/lower_bound, including with workaround for clang.
https://gitflic.ru/project/erthink/bsearch-try
SC
15:56
Simon C.
In reply to this message
Awesome thanks for the translation 💜
Л(
16:00
Леонид Юрьев (Leonid Yuriev)
Actual benchmark on Xeon 5317 with GCC 11.3:

| Function         |   Time    |  Diff    | Ratio |
|:-----------------|----------:|---------:|------:|
| ly_bl_mini1 | 0.019064 | -73.94% | 3.84 | <<< current libmdbx
| ly_bl_mini2 | 0.019264 | -73.67% | 3.80 |
| ly_bl_goto1 | 0.019273 | -73.66% | 3.80 |
| ly_bl_mini0 | 0.019464 | -73.39% | 3.76 |
| lb_PVKaPM | 0.019605 | -73.20% | 3.73 |
| ly_bl_goto2 | 0.019751 | -73.00% | 3.70 |
| ly_bl_mini3 | 0.020070 | -72.57% | 3.65 |
| ly_bl_clz_goto | 0.020107 | -72.52% | 3.64 |
| Malte Skarupke | 0.024787 | -66.12% | 2.95 | <<< implementation from the acticle
| ly_bl_switch2 | 0.026909 | -63.22% | 2.72 |
| ly_bl_switch1 | 0.031205 | -57.34% | 2.34 |
| ly_bl_clz_switch | 0.032492 | -55.59% | 2.25 |
| bsearch_libmdbx | 0.071366 | -2.45% | 1.03 | <<< old libmdbx
| bsearch_linux | 0.073035 | -0.17% | 1.00 |
| std::lower_bound | 0.073157 | +0.00% | 1.00 |
| bsearch_stdlib | 0.075274 | +2.89% | 0.97 |
🥰
SC
16:01
Simon C.
crazy 🤯💪
😁
Л(
16:04
I posted a comment (didn't need any account or verification). Needs to be reviewed though
👍
Л(
yh
16:21
yi huang
Awesome 👏, I need to check if it works in golang
Л(
16:28
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Algo will works, sure.
But doubt aboUt workaround for clang's codegen.

Also please read/translate the README notes.
For instance, in my (inside libmdbx) cases it is not a problem to read/access one more item beyond the given vector length.
yh
16:51
yi huang
I thought it rely on some compiler optimizations? I’ll do some benchmarks on golang later
Л(
17:03
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Most compilers have some problems with CMOV on x86 for historical reasons.
Intel has changed the CMOV implementation inside its processors several times, as well as repeatedly redesigned/improved branch prediction and out-of-order execution.
Thus for now, most compilers avoid using CMOV or have incorrect/suboptimal heuristics for it.

For instance, clang needs an workaround to be forced to use CMOV instructions.
VS
yh
yh
19:11
yi huang
A naive port to golang don't show any improvement against the builtin version though, https://gist.github.com/yihuang/1acbfef3a0dccb8a0fc024e1b3ad00f6
Л(
19:26
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Oh, you are missed a lot of things.

For instance, compare this C snip with your Go code.
19:34
Basically, you should got similar code to see a performance boost.
yh
19:35
yi huang
I was trying to benchmark binsearch using bytes compare
19:35
maybe the cmp is just too heavy
19:37
https://go.godbolt.org/z/3TY6TGhhE
it seems do generate CMOVQNE instruction though
Л(
19:38
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Yes of cause, you should avoid indirect calling comparison function, it must be just a single CMP instruction.
yh
19:39
yi huang
so it can only be used with integers basically?
Л(
19:43
Леонид Юрьев (Leonid Yuriev)
In reply to this message
For native CPU types only.

It is obvious, since for most other cases a (relatively) complex comparison function is required.
So an overhead of comparison will be much large than spending inside the binary-search loop.
yh
19:45
yi huang
In reply to this message
Yeah, makes sense, thanks for your explanation
Deleted invited Deleted Account
3 May 2023
W invited W
5 May 2023
MM
17:54
Melbourne Channel Moderator
In reply to this message
I wonder how stable it is now.
Can we trust financial data with libmdbx?
Л(
18:26
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Hmm, I can't understand:
- how do you link the benchmark results of binary search functions with context of a trust ?
- what stability are you talking about ?
- how can shown results affect the durability while storing data (no matter financial or any other) ?
MM
18:36
Melbourne Channel Moderator
In reply to this message
Because I am not the best programmer, normally the fast things I write breaks fast.

1. Not sure how much reliability is tested against the benchmark that's all.
2. Libmdbx takes more storage than rocksdb? So wondering if this speed increases storage size etc.
18:40
In reply to this message
U are extremely good programmer no doubt and libmdbx is state of the art
18:41
Hows mithrildb doing just curious coz been a while
18:41
Is mithril a nosql, newsql or rdbms?
6 May 2023
Алексей invited Алексей
Л(
23:51
Леонид Юрьев (Leonid Yuriev)
In reply to this message
> Because I am not the best programmer, normally the fast things I write breaks fast.
> 1. Not sure how much reliability is tested against the benchmark that's all.

That benchmark evaluates the speed of auxiliary/utilitarian search functions. Programmers like such challenges forever.
This is not related to reliability of libmdbx, provided/expecting the selected function will work correctly.
So, the benchmark includes a test for verification a search functions, since we wanna a correct and fast search, rather that just a fast one.
An the mentioned test revealed an inaccuracy in the behavior of the function described in a trustworthy article.

> 2. Libmdbx takes more storage than rocksdb? So wondering if this speed increases storage size etc.

A hammer and a screwdriver are both tools, but for different purposes.
libmdbx and rocksdb are both storage engines...

libmdbx is B-tree based, with ACID via MVCC+COW+Shadow paging, without WAL, but mostly non-blocking for readers etc.
RockDB is LSM based, with set of features, including compression...
So, ones are both storage engines, but explaining all the differences is only a little easier than explaining how the universe works.

> U are extremely good programmer no doubt and libmdbx is state of the art

Thanks.

> Hows mithrildb doing just curious coz been a while
> Is mithril a nosql, newsql or rdbms?

Briefly MithrilDB is storage engine similar to libmdbx, but better.
In particular is (re)solves few fundamental architectural issues inherited from LMDB.
Nonetheless, I try not to make any promises regarding MithrilDB until release.
Just git grep -i mithril for more info.
7 May 2023
Л(
07:06
Леонид Юрьев (Leonid Yuriev)
In reply to this message
На всякий, для информации.
Malte Skarupke ответил 1 и 2.

+ Еще пара дополнений:
а) По просьбе Алексея Копытова в бенчмарк добавлен линейный поиск, который оценивается пока размер N ≤ 1024 (можно задать опцией --max N).
На небольших размеров на современных x86 работает неплохо, в среднем быстрее std::lower_bound, но далеко не лучше всех.
б) Поправлена реализация из статьи "ARRAY LAYOUTS FOR COMPARISON-BASED SEARCHING".
в) Дополнен README и т.п.
👍
СМ
8 May 2023
alex b invited alex b
9 May 2023
Kavan invited Kavan
12 May 2023
SC
19:49
Simon C.
I have a weird problem. I tried the following:
When I get a MDBX_MAP_FULL error in a write txn I increase the size_upper using mdbx_env_set_geometry. This works fine. When I retry the put() in the same transaction however I get MDBX_BAD_TXN.

Any idea what could be wrong?
СМ
19:53
Сергей Мирянов
Not an answer to your question. But when I need to grow a DB I just call mdbx_env_set_geometry one on DB open.
SC
19:57
Simon C.
Because all Android and iOS phones have very limited (and fluctuating) virtual memory available (contrary to what you might assume)
Л(
19:58
Леонид Юрьев (Leonid Yuriev)
In reply to this message
On MDBX_MAP_FULL current write transaction enters into erroneous state and should me aborted,
The reason for this is that it is too hard undo all page operations, since the MDBX_MAP_FULL could be occurred deeply on split/merge/re-balance b-tree operation stack.
So it is reasonable just to abort/cancel entire transaction instead of undo the last operation.
SC
19:58
Simon C.
That makes sense, thanks!
СМ
19:59
Сергей Мирянов
In reply to this message
You just need to set growth_step and don't set upper limit.
SC
20:00
Simon C.
That's for file size. Afaik mdbx will always mmap the full amount
СМ
20:02
Сергей Мирянов
In my scenarios it remaps when more space needed.
I began from 4M and grow to 100G+
SC
20:04
Simon C.
🤯 so you just set size_upper to -1?
СМ
20:07
Сергей Мирянов
mdbx_env_set_geometry(env, 0, 0, size_upper, growth_step, -1, -1);

growth_step=65536 * 64, size_upper=1024 * 1024 * 1024 * 256
20:10
But according to docs you can just keep it -1

The upper bound of database size in bytes. Zero value means "minimal acceptable", and negative means "keep current or use default". It is recommended to avoid change upper bound while database is used by other processes or threaded (i.e. just pass -1 in this argument except absolutely necessary). Otherwise you must be ready for MDBX_UNABLE_EXTEND_MAPSIZE error(s), unexpected pauses during remapping and/or system errors like "address busy", and so on. In other words, there is no way to handle a growth of the upper bound robustly because there may be a lack of appropriate system resources (which are extremely volatile in a multi-process multi-threaded environment).
SC
20:11
Simon C.
Doesn't work for me 😔️️️️️️ For -1 I soon get a MDBX_MAP_FULL and when I pass a large size it mmaps the whole range.
😢
СМ
20:12
Deleted Account
Yes, -1 sets size_upper to 2MB, default
👍
СМ
SC
13 May 2023
K
07:01
Kavan
I was trying to implement batches for mdbx. Can anyone help me in this? Or can anyone suggest how do I proceed with it?
07:02
I am trying to implement go-ethereum with mdbx as the database. As leveldb and pebbledb have batches, I need that in mdbx.
yh
07:03
yi huang
mdbx has transactions
07:04
For go-ethereum with mdbx, check erigon
K
07:06
Kavan
In reply to this message
I have checked in erigon but they have implemented differently.
I need to have batches for geth to work. Coz batches are being used many a times.

So batches ~ txns?
AV
07:13
Artem Vorotnikov
In reply to this message
it took Erigon ~5 years to efficiently transition from leveldb to mdbx and adjust its architecture to work

I think you have an XY problem - maybe ask Erigon devs what business logic you want to see in the node?
👍
K
Л(
Л(
07:14
Леонид Юрьев (Leonid Yuriev)
In reply to this message
With mdbx, you will always use transactions.
Then you can do one insert/update/remove per transaction or a lot of.
MM
14:28
Melbourne Channel Moderator
Page size: a power of 2, minimum 256 (mostly for testing), maximum 65536 bytes, default 4096 bytes.
Database size: up to 2147483648 pages (≈8.0 TiB for default 4K pagesize, ≈128.0 TiB for 64K pagesize).

1. what does page size mean if i choose 64kb? and my key values are 128bytes in total? does that mean
128TB/128bytes, roughly 1000000000000 items i can store?
VS
14:31
Victor Smirnov
In reply to this message
Page size is a btree block size. Number of pages in a DB is limited with a 32 bit number. So, the larger page, the more keys/values can be stored in the DB. But with larger pages, writes are slower (but reads may be faster)
MM
14:33
Melbourne Channel Moderator
In reply to this message
128TB doesnt sound like a lot nowadays. i know libmdbx can operate with memory size much smaller than actual storage size. was wondering if should be 1gb ram to 100gb storage ratio or 1gb ram to 1tb storage ratio? what's the best ram to storage ratio to use in libmdbx?
VS
14:37
Victor Smirnov
Actually, mmap as a storage layer means that your working set should fit in RAM. Otherwise, YMMV.
MM
14:37
Melbourne Channel Moderator
i have implemented both libmdbx solution and terarkdb (for write optimized)

i am using terarkdb more but always have this mental conflict on why i should really use libmdbx. does anyone have a very good reason to use libmdbx?

the rationale other than extreme read speed is... the write speed is of course not as optimum than lsm db. so... under what circumstances should people really use libmdbx? the only way i see it is fewer writes than reads db. like 1 write to 1000 read ratio kind of thing.
14:39
In reply to this message
so means libmdbx must hv the same amount of ram as the db size?
VS
14:42
Victor Smirnov
Working set may be much smaller than DB size, depending on your task.
yh
14:42
yi huang
Working set means the “hot” data I guess
👍
VS
MM
14:43
Melbourne Channel Moderator
what ram to db ratio is a good suggestion for libmdbx?
yh
14:45
yi huang
Recently I use mmap to implement a merkle tree, I rolled my own WAL to handle writes, the snapshot is immutable and mmap-ed, recreated periodically, I think the design fits blockchain use case very well.
VS
14:55
Victor Smirnov
In reply to this message
It’s general question lsm vs cow. Feature sets are very different. If lsm fits your requirements, stick with it.
Л(
15:07
Леонид Юрьев (Leonid Yuriev)
In reply to this message
No such gold ratio.

Imagine you have a 1Tb DB and 1Gb RAM but with three different access patterns:
1) 99.99% reads hits into cached data.
2) 50% random reads and 50% reads with hit into cached data.
3) Random reads with true random uniform distribution.

All these cases will work with most any kind of storage engine.
But for memory-mapped DB (like MDBX) the first case is good, since whole available RAM works/used like a cache.

So for the first case you will get superior performance with libmdbx, since it is no overhead for access to cached data.
On the contrary, in the third case, libmdbx will loose in performance, since any cache miss will cause a page fault (context switch into kernel mode), then serialized read from a disk and altering a PTE (page table entry).

Thus there is no "golden ratio", but there are suitable and unsuitable use cases.
VS
15:08
Victor Smirnov
CoW has fundamentally higher WAF, so SSD wears faster than with LSM. This is the price we pay for CoW’s features.
Л(
15:30
Леонид Юрьев (Leonid Yuriev)
In reply to this message
This is not always true, at least the difference in WAF may not be very big.

LSM can have a cumulatively larger WAF taking in account a deferred merges, especially when many long values are stored.
On the other hand, b-tree with short keys will have a low height, which could significantly reduce WAF.

+ Beside of above, most of LSMs internally uses an append-only approach for writing files, i.e. ones are CoW-like.
MM
15:34
Melbourne Channel Moderator
other than blockchain scenario, what other stuff you guys are using libmdbx? also why would lsm tree be better etc. just curious.
15:37
Deleted Account
I use mdbx to implement a object oriented db for ruby
VS
15:44
Victor Smirnov
In reply to this message
Projects concerning with WAF should measure their db engines, and it’s true that lsm is not always that much better as they say. Ioarena + nvme tools may give some numbers.
👍
Л(
15 May 2023
Л(
14:14
Леонид Юрьев (Leonid Yuriev)
There are question about benchmark(s) and some comments.
https://gitflic.ru/project/erthink/libmdbx/issue/12
👍
СМ
16 May 2023
Л(
10:06
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Было фото схемы "секретного китайского лазера" (шучу) - похоже на отправку по ошибке, удаляю.
Seems a mistake, removed.
19 May 2023
Robatipoor invited Robatipoor
R
17:52
Robatipoor
Hello, Is it possible to find the list of names of all tables in the libmdbx database?
СМ
18:02
Сергей Мирянов
mdbx-stat или mdbx-dump
R
18:18
Robatipoor
In reply to this message
Thanks and is there a Json dump for this database?
СМ
18:20
Сергей Мирянов
I'm not sure about, but I think there is no json export
🙏
R
Л(
18:30
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Please submit an issue to gitflic, so that I don't forget to add this to the API.
👍
VS
🙏
R
20 May 2023
MM
01:14
Melbourne Channel Moderator
In reply to this message
These comments suggest it is faster to use lmdb? Coz 2 vs 3 meta data seems to suggest lmdb is better
Л(
09:39
Леонид Юрьев (Leonid Yuriev)
In reply to this message
The number of meta pages does not affect the WAF, since only 1 of ones is updated/written during a commit.
The impact on reading is a little more, since we need to look at all the meta pages, but MDBX minimizes code to analyze ones and all are cached in RAM mostly cases.

On the other hand, the 3 meta pages allow not to spoil steady commits when reordered and/or asynchronous writing to disk.
See MDBX_SAFE_NOSYNC description and https://libmdbx.dqdkfa.ru/dead-github/issues/266.

Nonetheless, "do nothing" will be even faster ;)
MM
20:28
Melbourne Channel Moderator
What's the ram to disk ratio suggestion for libmdbx?
I was thinking 1gb ram to 100gb disk store ok?
Л(
20:49
Леонид Юрьев (Leonid Yuriev)
In reply to this message
No such gold ratio.

Imagine you have a 1Tb DB and 1Gb RAM but with three different access patterns:
1) 99.99% reads hits into cached data.
2) 50% random reads and 50% reads with hit into cached data.
3) Random reads with true random uniform distribution.

All these cases will work with most any kind of storage engine.
But for memory-mapped DB (like MDBX) the first case is good, since whole available RAM works/used like a cache.

So for the first case you will get superior performance with libmdbx, since it is no overhead for access to cached data.
On the contrary, in the third case, libmdbx will loose in performance, since any cache miss will cause a page fault (context switch into kernel mode), then serialized read from a disk and altering a PTE (page table entry).

Thus there is no "golden ratio", but there are suitable and unsuitable use cases.
👍
VS
21 May 2023
SC
08:07
Simon C.
The docs for mdbx_cursor_del say "Both MDBX_NEXT and MDBX_GET_CURRENT will return the same record after this operation."

Is that also true for other cursors pointing to the same key/value?
AL
09:35
Andrea Lanfranchi
In reply to this message
Only if are cursors bound to the same transaction of the cursor which have performed the deletion, on the same subdb and that were pointing to the same key being deleted
👍
Л(
SC
25 May 2023
Pavel Vragov invited Pavel Vragov
26 May 2023
A
14:14
Aртём
Как себя будет чувствовать базейка если будет фиксировано 100к ключей, а значение ключа до 700кб. Или libmdbx заточена под маленькие значения до 5-10кб? Спасибо.
VS
14:24
Victor Smirnov
Никак. Это же btree, он в большие ключи не умеет. Но можно сделать поверх btree хэш-таблицу. И тогда можно работать с большими ключами.
Л(
14:33
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Ну под маленькими (короткими) ключами/значениями обычно подразумевается не более сотни байт, но не суть.

Для ключей есть ограничение на максимальную длину (примерно 1/2 от размера страницы), rtfm limitations.

Для значений в зависимости от из размера/длины есть три случая:
- до длины примерно 1/2 страницы БД = ключи могут иметь много (миллионы) значений, который хранятся во вложенных b-tree, rtfm MDBX_DUPSORT.
- до размера в одну страницу = значения выносятся в отдельные "overflow/large" страницы, всё хорошо.
- для более длинных значений = значения хранятся в последовательных страницах, тут могут быть пенальти, так как b-tree склонно с фрагментации, rtfm "Large data items".
VS
14:34
Victor Smirnov
А, я видимо не распарсил "значение ключа")
Л(
14:35
Леонид Юрьев (Leonid Yuriev)
In reply to this message
тут может подразумеваться любой из двух смыслов )
A
14:35
Aртём
Не верно задал. Всего ключей 100к. А вот значения(не ключ, value) от 10к-700к
Л(
14:37
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Всё равно не понятно.
Вы спрашиваете про максимальное количество или про максимальную длину каждого отдельного элемента ?

По количеству - всё хорошо.
По длине - ответил выше.
A
14:37
Aртём
Сам ключ длинной 8байт. Именно вэлуе интересует.
14:38
In reply to this message
Макс длинну самого элемента и как вообще будет себя чувствовать при конкурентном чтение в 8-12потоков.
VS
14:40
Victor Smirnov
In reply to this message
по чтению всё должно быть нормально
A
14:40
Aртём
Идея в том что бы маленькие файлики загнать в KV.
Л(
14:53
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Про длину ответил выше.

Чтение не блокируется (только при регистрации треда при старте первой транзакции чтения) и линейно масштабируется по ядрам до забивания пропускной способности ОЗУ.
НО! это пока вся БД или хотя-бы горячая часть данных помешается в ОЗУ.

Если же данных сильно больше, то будут page faults.
Всё будет работать, но в предельной ситуации при случайном доступе к огромной БД, это будет медленнее чем хорошо реализованное чтение через io_ring.

Причина в том, что в memory mapped БД "подъем" холодных данных (вне кэша в ОЗУ) с диска происходит через page fault, что требует:
- переключение контекста ЦПУ в режим ядра ОС;
- чтение страницы с диска с ожиданием результата;
- изменение PTE c блокировкой соответствующей таблицы страниц виртуальной памяти с захватом блокировки;
- переключение контекста ЦПУ назад в режим приложения.
Это порождает больше накладных расходов и требует сериализации (последовательно выполнения) читающих запросов с диска.

Тем не менее, если данные помещаются в ОЗУ (а ядро ОС автоматически обеспечит более-менее разумное кеширование с использованием всей доступной памяти), то libmdbx даст производительность близкую к теоретическому пределу.
👍
СМ
A
A
14:58
Aртём
In reply to this message
Спасибо за развёрнутый ответ!
👍
Л(
2 June 2023
Alain invited Alain
3 June 2023
A
17:32
Alain
Recently updated to 0.12.5 from 0.11.1.19 and noticed extremely longer time to release large amounts of cursors for read-only transactions. Since MDBX has more flexibility than LMDB did for cursor re-use, decided to make use of it and make it part of the Java bindings that we publish.

I am experiencing some problems that seem to be related mostly to not fully understanding the lifecycle of the cursors. For example, I will hit an assert about not finding the cursor if a cursor is released from a transaction and re-bound to a new transaction while the previous transaction is still ongoing. It seems from my basic understanding that the cursors are somehow released by cursors_eot at the end of a transaction.

So is there a good description of how and when we are allowed to reuse a cursor. Does the transaction have to end ? Can it be reused within the same transaction? (seems to be yes), what about in same transaction bound to a different db? etc.
Л(
17:38
Леонид Юрьев (Leonid Yuriev)
In reply to this message
It's great that you noticed this feature and plan to make it available to users of your bindings!

However, I would like to clarify: have you just noticed that releasing cursors takes time, or have you noticed that in version 0.12.5 it takes longer compared to 0.11.x ?
A
17:42
Alain
In reply to this message
In version 0.12.5 i takes much longer. For example a read-only txn would get 187454 cursors and release them at txn end which would take ~90ms, with 0.12.5 it is ~11m30s. Code change shows that there was a commit last year that I suspect to be the reason: "mdbx: adding cursors tracking/lists for read-only transactions."
Л(
19:16
Леонид Юрьев (Leonid Yuriev)
In reply to this message
There is a set of singularities/traits inherited from LMDB that are not particularly clear how to fix without breaking the compatibility and/or creating a new side-effects.

For instance the case with nested transactions and closing a cursor of a parent transaction while a nested/child transaction is running.
Historically, LMDB provides inheritance of the state of parent transactions (including cursors) of a nested/child transaction and at the same time opening new cursors that are inherited to the further nested transaction.
This arises a few snags with cursors tracking and shadowing/restoring of ones, etc.

--

For now libmdbx tracks cursors completely and allows flexibly reuse ones.
Some disadvantages currently is that a singly linked list is used for tracking.
New items are added to the head of this list (which is very cheap), but deleting requires scanning the list from the head to the item being deleted.
Thus releasing/closing cursors in the same order of ones were created leads to full scan this single-linked list for each cursor being closed.

I will try to fix this (switch to a doubly linked list) in 0.13.x and probably add a functions like mdbx_txn_release_all_cursors(bool unbind_not_close) and mdbx_cursor_unbind(cursor) to the API.
So far, as a workaround, I advise you to close the cursors in the reverse order of ones creation.

--

About cursor reusing:
Briefly - all cases you are noted should not be triggered an issue.

1. each created cursor should be closed explicitly;
2. cursor and/or transaction should not be used concurrently from a several threads, this includes hidden/implicit using a transaction during detaching cursor from linked list during closing it or binding it another transaction.
3. you could re-bind a cursor any time until it closed.
19:17
I guess your code violates the rule #2 above.
A
19:22
Alain
In reply to this message
Great, as a first step, I surely can close all the cursors in reverse order, that shouldn't be too difficult and then really dig into the reusing.
Л(
19:31
Леонид Юрьев (Leonid Yuriev)
However, it is almost always unwise to perform multiple read transactions at the same time, except when these transactions work in different threads and do not mix.
If you still do this, then you can inadvertently mix the data read in different transactions and corresponding to different MVCC snapshots (different versions of data).
And if you have several transactions reading the same MVCC-snapshot, then this is just an extra resource cost.

Therefore, libmdbx suggests following the "one transaction = one thread" rule and does not allow its violation without specifying the MDBX_NOTLS option.
A
19:36
Alain
In reply to this message
Yes, this is what is done, but in the stated example, it leads to 1 txn running on 1 thread performing a huge number of queries each with their own cursor at the moment. Might be possible to have a few txn/threads read all that in since its read only here and many cases are not sensitive to a single snapshot, but figuring that out can be difficult and for RW txn, well it makes no sense IMHO.
19:44
In reply to this message
More than likely, but I find it strange that while mdbx_cursor_create requires no transaction, mdbx_cursor_close checks to unlink if C_UNTRACK which seems to be true after a bind and normally reset at cursors_eot, which IMHO implies that the txn & thread would need to match when finally closing the cursor once it has been determined not to be needed anymore.
A
20:25
Alain
In reply to this message
I just did and got a big improvement, but still nowhere like it was:
1m:24s:265ms
Closing cursors as they become unused is faster but we had an issue in 2018 that was related to an assert issue with MDBX_MC_READY4CLOSE that forced us to switch approach, but this is too old to get much more details.
Л(
20:31
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Historically mdbx_cursor_open() creates a cursor binded with a transaction, and mdbx_cursor_close() performs untracking tasks, but both only for write-transactions.
Later added mdbx_cursor_create() creates an unbinded cursor, and mdbx_cursor_bind() internally uses C_UNTRACK to check is cursor binded or no.
20:40
In reply to this message
In case of an assertion failure you should report an issue. Please do this at next time.

So far I have no idea why closing (even in reverse order) takes so much time, if after all the closing order is not completely reversed.
Inside mdbx_cursor_close() is a fairly simple code with one loop (which should not be executed, in the case of reverse order of closing) and single call to free().
4 June 2023
A
20:05
Alain
In reply to this message
Well added a method similar to what you suggested, but simply to close all cursors for a transaction and my use case of 187k cursors is now ~75ms (release mode 39ms). And one with 18k is 1ms and most everything else is reporting 0ms (would have to go nanos).
So the issue is not in the mdbx code, but appears to be in change in method call overhead maybe.
Here's the very naive method I added:
int mdbx_txn_release_all_cursors(const MDBX_txn *txn) {
int cnt=0;
if (likely(txn)) {
ENSURE(txn->mt_env, check_txn(txn, 0) == MDBX_SUCCESS);

for (size_t i = FREE_DBI; i < txn->mt_numdbs; ++i) {
MDBX_cursor *headmc = txn->mt_cursors[i];
MDBX_cursor *tmpmc;

while(headmc != NULL) {
tmpmc = headmc;
headmc = headmc->mc_next;
mdbx_cursor_close(tmpmc);
cnt++;
}
}
}
return cnt;
}
and everywhere the returned count matches what was expected.

My next step is to investigate the cursor bind/renew stuff now that I have something that is back to operating status. I exposed the MDBX_cursor struct so that I can access the internals from Java and will use that to better assess what is going on.

Thanks for the input
👍
Л(
5 June 2023
A
13:19
Alain
This might be a stupid question, but rarely working on C/C++ projects, I am a bit at a lost as to how to set MDBX_DEBUG. When building in debug mode, it is turned on AFAICT by NDEBUG not being defined. But why isn't there any option in CMAKE to set it, even for release build or is that such a bad idea, as I was thinking of doing that and then controlling the level via mdbx_setup_debug, which would allow us to dynamically change log level at runtime. I want to avoid if possible having to pass it on the command line which can always be done.
Л(
15:06
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Enabling MDBX_DEBUG is quite expensive in terms of performance and code size.
So for libmdbx users it is reasonable to control it via NDEBUG, and manually adding corresponding macro definition in other/extra cases.

i.e. it is proposed to use either a non-fast debug build or a fast non-debug build, because there is no third option.
👍
A
10 June 2023
11:16
Deleted Account
Will a query call, ever use more stack over ,16k?

Only read by int key
11:18
Need run from a limit size stack routine
Л(
11:32
Леонид Юрьев (Leonid Yuriev)
In reply to this message
mdbx_get() uses on-stack cursor to avoid allocation.
So just use cursors API for your case.
11:33
Deleted Account
So just call mdbx get, and start stop txn will be safe for Small stack case?
11:54
Deleted Account
OK, I need create cursor my self from heap
13 June 2023
Isla fuerta invited Isla fuerta
R
10:17
Robatipoor
Can anybody explain to me, as simply as possible, what this error code means?
"MDBX_BAD_DBI: The specified DBI-handle is invalid or changed by another thread/transaction"
AS
10:23
Alex Sharov
In reply to this message
R
12:06
Robatipoor
In reply to this message
Unfortunately I can't see any useful information.
AS
12:07
Alex Sharov
In reply to this message
Once a transaction has been created, a database (i.e. key-value space inside the environment) can be opened within it using mdbx_dbi_open(). If only one database will ever be used in the environment, a NULL can be passed as the database name. For named databases, the MDBX_CREATE flag must be used to create the database if it doesn't already exist. Also, mdbx_env_set_maxdbs() must be called after mdbx_env_create() and before mdbx_env_open() to set the maximum number of named databases you want to support.
🙏
R
Л(
12:08
Леонид Юрьев (Leonid Yuriev)
In reply to this message
AL
12:08
Andrea Lanfranchi
Using an analogy with relational databases a dbi is a table
👍
R
12:08
A container of logically grouped data
15 June 2023
09:16
Deleted Account
I get one question for use mdbx with GO:

I see mdbx use signal_handler, golang require use SA_ONSTACK or it could crashed. https://stackoverflow.com/questions/47869988/how-does-cgo-handle-signals


So to link with go, the signal_handler should be patched? what about windows use case (seems windows mingw not provide SA_ONSTACK)?
👍
09:30
The signal_handler seems not use stack, so it should be save to link with GO?
AS
11:22
Alex Sharov
In reply to this message
As i understand (maybe wrong a bit): you mean - mdbx using pthread_cond_signal - so, it's not about signals - it's about "thread-local variable" for write-transactions.
It means in golang you need use: runtime.LockOSThread right before begin write-transaction (and unlock after abort/commit).
12:34
Deleted Account
You are correct about TLS should LockOSThread.

My question is about use signal handle may cause go runtime run at wrong stack(too small for c function call). I guess if the signal handler not use big stack it should not be a problem.
AS
13:13
Alex Sharov
In reply to this message
tbh, i don’t know - never really faced this problem.
16 June 2023
Л(
14:19
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Hmm, please clarify/specify exactly where you saw the use of the signal handler (i.e. the signal() or sigaction() calls) inside the libmdbx library (please do not confuse it with command-line utilities nor tests).
14:25
Deleted Account
Thanks for explain. I just do a search and see the handle. (Now I confirm this is from command-line files)
Л(
21:21
Леонид Юрьев (Leonid Yuriev)
👍
MM
VS
СМ
4
19 June 2023
Л(
03:06
Леонид Юрьев (Leonid Yuriev)
Earlier, I noted that will be snaply express my position here on fundamental political issues.
So it a time.

IN:
Justification = my personal meaningful position based on both open and closed sources.
Objective = less lies and violence, less confrontation, an avoidance of the destructive clash.

OUT:
The main obstacle = the Lie and obstruction of truth (at least alternative) information.

... be reasonable, use your brain and dig.
Regards.
22 June 2023
Nikita Demerza invited Nikita Demerza
ND
12:10
Nikita Demerza
12:11
не могу понять, в чём проблема
12:12
собирал из репозитория, как обычно:
make
sudo make install
с тестами никаких проблем нет
12:13
Л(
15:09
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Вы не задали библиотеку для линковки
MK
15:47
Max Kozlov
здравствуйте, подскажите плз, может есть какие-нить статьи, чтобы почитать в каких задачах libmdbx хорошо применять?
AS
16:00
Alex Sharov
In reply to this message
https://www.youtube.com/watch?v=5DDEIyfw0RU
https://www.youtube.com/watch?v=tEa5sAh-kVk
https://www.youtube.com/watch?v=Rx1-in-a1Xc

думаю все статьи в интернете про lmdb будут валидны
👍
VS
MK
16:15
Max Kozlov
спасибо =)
16:16
а про мифрилДБ новости есть? хотелось бы понять если станет доступной, то хотя бы приблизительно когда?
ND
16:16
Nikita Demerza
In reply to this message
А как это сделать?
AS
ND
16:56
Nikita Demerza
А можно как-нибудь в лд добавить?
Victor Kazarinov invited Victor Kazarinov
23 June 2023
𝓾𝓷𝓭3𝓯𝓲𝓷𝓮𝓭 invited 𝓾𝓷𝓭3𝓯𝓲𝓷𝓮𝓭
?
18:19
𝓾𝓷𝓭3𝓯𝓲𝓷𝓮𝓭
Say 👋 with English 🙂
26 June 2023
ND
12:28
Nikita Demerza
я пытался в одном окружении создать 8 бд, сделал 8 разных по значению dbi но после mdbx_dbi_open они все приравнялись единице и я вместо 8 бд получил 8 ссылок на одну и туже бд, как это исправить?
12:33
for (int i = 0; i < 8; i++)
{
txn = NULL;
mdbx_txn_begin(env, NULL, 0, &txn)
dbis[i] = i;
printf("%d",dbis[i]);
mdbx_dbi_open(txn, NULL, MDBX_DB_DEFAULTS, &dbis[i])
printf("%d\n",dbis[i]);
mdbx_txn_commit(txn);
}
12:33
что тут не так?
СМ
12:34
Сергей Мирянов
mdbx_dbi_open(txn, NULL, MDBX_DB_DEFAULTS, &dbis[i]) - NULL
12:34
вам стоит внимательнее RTFM
ND
12:35
Nikita Demerza
In reply to this message
если задаю имя какое-то, то вылазит ошибка
12:38
-30791
СМ
12:40
Сергей Мирянов
прочитайте про mdbx_env_set_maxdbs
👍
Л(
ND
12:46
Nikita Demerza
теперь по непонятным причинам mdbx_dbi_open выдаёт по-моему совсем не относящуюся к этой функции ошибку - -30798
СМ
12:47
Сергей Мирянов
вам стоит перестать пробовать и прочитать документацию.
в вашем случае ошибка потому что вы пытаетесь открыть dbi который еще не создан.
👍
Л(
ND
12:49
Nikita Demerza
так mdbx_dbi_open для открытия или СОЗДАНИЯ бд, это в документации написанно
12:49
если нет, то как создать тогда?
СМ
12:50
Сергей Мирянов
прочитать про table_flags у этой функции.
но в целом это несерьезно.
👍
Л(
ND
12:52
Nikita Demerza
а что серьёзно?
Л(
12:53
Леонид Юрьев (Leonid Yuriev)
In reply to this message
RTFM до бесполезных вопросов
27 June 2023
ND
19:22
Nikita Demerza
@erthink, а почему с каждой новой транзакцией нужно заново открывать базы данных, почему нельзя один раз открыть бд в начале программы, а потом уже запускать транзакции?
19:29
когда можете принять участие в созвоне, чтобы обсудить этот и другие технические вопросы голосом?
Л(
23:45
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Всё совсем не так.

Исторически есть небольшая путаница с термином "БД", которая обусловлена некоторым наследованием стилистики API, начиная от dbm, к Berkeley DB, затем к LMDB и потом к libmdbx.
В зависимости от контекста под "БД" может пониматься как одна "таблица" ключ->значение, либо "БД" как файл на диске, в котором может быть несколько таблиц ключ->значение.
При этом для dbm такой путаницы нет, ибо там в одном БД (как файле) была одна БД (как таблица key-value).

На самом деле с этим нет каких-либо сложностей, если хоть немного RTFM.
Поэтому настоятельно советую поступить так:
- прочитать Getting started;
- открыть mdbx.h и полностью прочитать два раза;
- открыть mdbx.h++ и тоже полностью прочитать два раза;

После этого у вас не должно быть вопросов, а если они будут, то лучше сначала внимательно прочитать описание соответствующих функций/типов/структур/опций и уже после задать вопросы.
28 June 2023
Л(
00:10
Леонид Юрьев (Leonid Yuriev)
In reply to this message
В основном Вы должны ориентироваться на документацию и собственные силы.
При этом приветствуются поправки/советы для улучшения документации, в том числе возможна подработка.
Кроме этого, практически все статьи/примеры/ролики по LMDB также применимы и к libmdbx с поправкой на доработки, добавленные возможности и разницу в префиксах.

Участвовать в созвонах - это только когда действительно есть что обсуждать/пояснять, но ни в коем случае не пересказ документации.
Если требуется какое-то интенсивное погружение, либо моё участие в проектировании и/или разработке, то в целом это не бесплатное развлечение (50К руб/час, минималка 2 часа, только в выходные).
Если у вас какой-то серьезный open-source проект, то условия будут другие (серьезный проект = Erigon, например).
+Для проектов государственных структур/органов РФ условия тоже другие (бесплатно, но через официальный запрос в Positive Technologies).
🔥
LP
СМ
AV
6 July 2023
taxifish invited taxifish
10 July 2023
MK
13:05
Max Kozlov
Здравстуйте. Подскажите плз, частое открывание и закрывание соединение с бд это случаем не антипаттерн для libmdbx?
Л(
13:07
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Да, это скорее антипаттерн.
Причём с любой БД.
MK
13:10
Max Kozlov
поясню немного задачу, чтобы контекст появился. Я в процессе изучения идеи подменить встроенный механизм хранения данных для гита пришел к тому, что для наших целей придется на каждый http запрос к сервису открывать соединение к хранилищу данных. Ну и собственно говоря в этот момент и уперся =) Но если это все же антипаттерн, тогда буду дальше размышлять что с этим делать =)
13:11
и кстати, судя по всему порт на джаву сдохший, код конечно на гитхабе остался, но вот скомпилированный вариант чет не доступен для подключения в проект через мавен
A
13:12
Alain
Hi I am the one who published the Java port and you are right that it is not on Maven as stated, but it is not dead and I could supply the jars if needed, while I'm looking at getting it on Maven.
MK
13:14
Max Kozlov
In reply to this message
hello, my colleagues and I are now preparing a registry of packages for maven for a public release. Later, I could help you upload it to us so that it is available to everyone =)
A
13:15
Alain
would appreciate, since this is a bit of a blocker for me at the moment
MK
13:16
Max Kozlov
In reply to this message
I understand you, then I'll come back here in a couple of weeks and help you figure it out
A
13:16
Alain
great
СМ
13:28
Сергей Мирянов
In reply to this message
у нас наверное похожая задача (с точки зрения хранилища) - мы в этой ситуации открыли одно соединения с БД (env) и открываем много read/write trx
MK
13:32
Max Kozlov
In reply to this message
тут просто хотелось бы отделить физически репозитории в разные хранилища. Чтобы они на диске в разных папках лежали
Л(
14:01
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Конечно я заинтересован чтобы Gitflic использовал мои наработки, но всё-таки нужно обратить внимание на такой вопрос:
- libmdbx работает быстро/быстрее, по-сути, только из-за того что не делает лишнего, т.е. минимизируя накладные расходы.
- отображение файла БД в память позволяет почти обнулить накладные расходы, но только пока объема свободного ОЗУ достаточно для горячих данных (либо для всех страниц БД).
- если же ОЗУ не достаточно для кэширования горячих данных, либо происходит преимущественно случаынй доступ к холодным данным, то отображение в память будет работать в минус.

Если страница с данным memory-mapped БД отсутствует в ОЗУ, то её загрузка будет происходить через page fault. При этом сериализуются (выполняются последовательно через одну очередь) все запросы на чтение/запись страниц из всех потоков всех процессов работающих с некоторой БД. Это порождает существенные накладные расходы, особенно если сравнивать с БД использующими асинхронный ввод/вывод на базе io_ring.
Особенно плохо, если в memory-mapped БД в таком сценарии хранятся большие/длинные записи (размером в несколько страниц) - тогда скорее всего будет отдельный page fault (с заходом в i/o-очередь к диску) для каждой страницы.

В реальности всё может быть несколько сложнее/запутаннее:
- файловый ввод/вывод может деградировать из-за мандатного контроля доступа (пример антивирусам и Astra-Linux).
- memory mapped обеспечивает автоматическое zero-cost кэширование, прием общее/разделяемое для всех потоков/процессов и интегрированное с менеджером виртуальной памяти ОС.
14:02
Если доводить до каких-то практических рекомендаций, то в вашем случае я бы рекомендовал примерно такое:
- реализовать абстракцию (интерфейс) реализующий доступ к key-valuе, с интегрированным кешированием/удержанием данных на время выполнения запроса/транзакции.
- под кешированием/удержанием тут понимается управление ссылками на считанные данные и буферы в ОЗУ, куда они были считаны.
- для libmdbx управление буферами в читающих транзакциях сведется просто к хранению указателей на отображенные в память данные, для пишущих транзакций чуть сложнее, см описание mdbx_is_dirty()
- для других не-memory mapped движков нужно будет отслеживание использование данные (счетчик ссылок и т.п.), но обычно это уже есть в привязках для высокоуровневых языков.

Тогда вы сможете менять движки хранения и/или ставить эксперименты.
14:02
Причем такая обвязка/абстракция будет иметь самостоятельную ценность.
MK
14:21
Max Kozlov
In reply to this message
спасибо за рекомендации, попробую отрефлексировать это все и в эксперементы превратить
Arihant invited Arihant
11 July 2023
yh
11:38
yi huang
is there a thing that store compressed data on disk, but provides uncompressed data through mmap, like doing decompression at kernel/filesystem level, it seems filesystem compression don't work with mmap transparently.
H
11:43
Hecate2
In reply to this message
NTFS?
AS
11:56
Alex Sharov
In reply to this message
yh
11:57
yi huang
thanks, i should give it try
11:58
I guess this kind of tech is useful for libmdbx as well ;D
12:13
it seems btrfs support it as well, nice
AS
12:25
Alex Sharov
In reply to this message
likely yes. but mdbx's b+tree gravitate towards random updates - so, can't use together with btrfs's auto-defrag feature. disable it.
12:28
In reply to this message
also on compressed fs's - probably better set in mdbx pagsize to 64kb - because fs's compression-block will be bigger anyway.
yh
12:42
yi huang
we've done a custom mmap based design to implement a merkle tree, mmap-ed immutable snapshot + write-ahead-logs, snapshot is rewritten periodically, seems perfect for our use case, especially with filesystem compression support 😄
12:49
Actually, I think the design can be applied to a generic kv db? the db persist the writes in logs and buffer the modified btree nodes in memory, mmap is only used in readonly way. it's like the redis but support out-of-memory data size
13:03
$ sudo compsize -x memiavl.db
Processed 865 files, 320027 regular extents (320027 refs), 416 inline.
Type Perc Disk Usage Uncompressed Referenced
TOTAL 41% 23G 56G 56G
none 100% 17G 17G 17G
zstd 14% 5.8G 38G 38G
reduced to 41% size doing nothing in code, not bad
Alexey Shekhirin invited Alexey Shekhirin
AS
14:10
Alex Sharov
In reply to this message
Why didn’t use existing LSM database?
yh
14:25
yi huang
Hmm, in my case of merkle tree, there’s an important difference that it can support accessing historical states by start from an old snapshot and replay logs, it’s not fast, but it don’t slowdown operations on the latest state which is the most frequent operations.
a normal implementation of versioned merkle tree on generic kvdb stores each tree node as a separate kv entry, which is too slow
14:27
I think even for generic case, it has the advantage of simplicity, and fast read and write performance, if the db is not too big and a total snapshot rewriting is acceptable
14:28
In reply to this message
I guess reading performance will be better than a lsm db, read performance should be close to a pure in-memory btree if cache is warm
☭k
19:57
☭ ktrace
In reply to this message
если рефлексия будет иметь отражение в коде - киньте ссылку :)
MK
20:02
Max Kozlov
In reply to this message
Окей, но пока у меня больше вопросов, чем идей как это вообще можно сделать 😄
16 July 2023
AV
19:22
Artem Vorotnikov
MI
🔥
yh
21 July 2023
Кац invited Кац
22 July 2023
R
20:16
Robatipoor
In reply to this message
Hi. I am using the library you wrote libmdbx-rs . Unfortunately, this library does not support async feature at the moment. Can you guide me how to add this feature to the project?
AV
20:17
Artem Vorotnikov
In reply to this message
All its types are Send + Sync so you're free to use libmdbx-rs in any async context.

What else do you need?
R
20:44
Robatipoor
In reply to this message
Directly calling blocking codes such as libmdbx-rs in the async context causes performance problems because it causes the runtime scheduler to be blocked, so it was suggested to use the spwan_blocking method in Tokyo Runtime, but the use of the spawn_blockig method makes the project very verbose. I want to convert the entire library to async version if possible.
AV
20:51
Artem Vorotnikov
In reply to this message
libmdbx-rs is merely a type-safe wrapper/ORM over MDBX which is entirely blocking code 🤷🏻‍♂️

On the other hand, MDBX should usually be so fast not to measurably block Tokio. Are you sure your database usage is fully optimized?
VS
21:01
Victor Smirnov
In reply to this message
You need a thread pool. Mmap is inherently blocking and there is no workaround.
21:03
Ideally, mmap-based DBs should be in a separate process accessed via an asynchronous transport (network/ipc/etc).
AL
21:51
Andrea Lanfranchi
Sorry if I drop in about this async thing but would like to understand more the context. A thread pool sets degree of parallelism not asynchronicity. Another thing if you want to have some wrappers around mdbx calls that can be made, for example, awaitable
21:53
Also async on RW transactions IMHO doesn't make much sense while access and handling of RO txes is so fast it doesn't require (again IMHO) much to halt a scheduler
21:55
Anyway an absolute judgement is impossible without further analysis of the context or the use cas
VS
21:57
Victor Smirnov
In reply to this message
It's all just to make non-blocking code working with blocking APIs (of mmap). True parallelism is when we have one thread per code in the pool. In our case (concurrency) the size of thread pool must be worst/average case of the simultaneous blocking IO operations. In practice, when we don't have strict QoS requirement, we may just access mmap from blocking code as is, because most of the time requests will be served out of RAM with zero latency.
23 July 2023
Yilong Li invited Yilong Li
26 July 2023
Senior Zhai invited Senior Zhai
4 August 2023
Жосслен Бомон invited Жосслен Бомон
ЖБ
04:16
Жосслен Бомон
Здравствуйте, у меня такой вопрос возможно ли хранить в базе 16ТБ данных и если да, то существует ли при этом какое-то требование к объёму ОЗУ?
СМ
04:35
Сергей Мирянов
In reply to this message
• Database size: up to 2147483648 pages (≈8.0 TiB for default 4K pagesize, ≈128.0 TiB for 64K pagesize).
04:36
In reply to this message
ЖБ
04:37
Жосслен Бомон
In reply to this message
Спасибо, это я видел, а есть пример конфигурации Env, а то я все время получаю ошибку переполнение mem
СМ
04:38
Сергей Мирянов
У меня нет
ЖБ
04:39
Жосслен Бомон
Возможно кто-то из участников чата скинет боевой конфиг
AS
05:27
Alex Sharov
In reply to this message
установить pageSize можно только до первого env_open

И можно mdbx_stat -e посмотреть - получилось ли
👍
ЖБ
Л(
ЖБ
18:17
Жосслен Бомон
А есть в чате участники которые применяют бд в продакшене?
Л(
18:20
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Да, libmdbx используется в продуктах http://ptsecurity.ru/ с 2017 года.
18:22
И если не ошибаюсь, то в Erigon c 2020 и в Reth с 2022 (с момента его создания/форка/увода из Akula).
ЖБ
18:22
Жосслен Бомон
In reply to this message
А вы не могли бы показать любой "живой" конфиг env, а то у меня работает только если я задаю многогигабайтные значения в set geometry
Л(
18:24
Леонид Юрьев (Leonid Yuriev)
Лучше покажите ваш код, с комментариями поясняющие ваше понимание/обоснование используемых значений.
ЖБ
18:24
Жосслен Бомон
Хотя я даже не успел заполнить базу даже на 0,0003%
18:25
In reply to this message
Да без проблем, сейчас покажу
Л(
18:25
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Вероятнее всего вы как-то неверно поняли описание одного из параметров
ЖБ
18:26
Жосслен Бомон
In reply to this message
Я уверен, что я неверно понял
ЖБ
19:12
Жосслен Бомон
package org.example;

import com.castortech.mdbxjni.*;

import java.util.HashMap;
import java.util.Map;

public class Main {

private static final long KB = 1024;
private static final long MB = 1024 * 1024;
private static final long GB = 1024 * 1024 * 1024;

public static byte[] IntToBytes(int value) {
return new byte[]{
(byte) (value >>> 24),
(byte) (value >>> 16),
(byte) (value >>> 8),
(byte) value
};
}

public static void main(String[] args) {

final int batchSize = 10000; // Задаем размер пакета
int count = 50_000_000;
byte[] emptyBytes = new byte[500]; // Пустой массив для теста
Map<byte[], byte[]> map = new HashMap<>(count); // Инициализируем карту

for (int i = 0; i < count; i++) {
map.put(IntToBytes(i), emptyBytes); // Заполняем карту
}

Env env = new Env(); // Создаем окружение
env.setMapSize(GB); // Задаем размер карты 1ГБ
env.setGeometry(-1, -1, 1024 * GB, 2 * GB, 1, KB); // эти настройки взяты с гитхаба
try {
EnvConfig envConfig = new EnvConfig();
envConfig.setUtterlyNoSync(true); // Отключаем синхронизацию
envConfig.setNoMetaSync(false); // Отключаем синхронизацию
envConfig.setNoMemInit(true);
envConfig.setNoTLS(false);

env.open("./database", envConfig);
Database db = env.openDatabase("test");

long startTime = System.nanoTime();
Transaction txWrite = env.createTransaction();
boolean ok = false;
try {
for (Map.Entry<byte[], byte[]> entry : map.entrySet()) {
db.put(txWrite, entry.getKey(), entry.getValue());
count++;
if (count % batchSize == 0) {
txWrite.commit();
txWrite = env.createTransaction();
}
}
if (count % batchSize != 0) {
txWrite.commit();
}
ok = true;
} finally {
if( ok ) {
txWrite.close();
} else {
txWrite.abort();
}
}
double totalWrites = (4 + (long) emptyBytes.length * (long) count) / 1024.0 / 1024.0; // Всего сырых данных

long endTime = System.nanoTime();
long duration = endTime - startTime;
double seconds = duration / 1_000_000_000.0;
double speed = totalWrites / seconds;
double op_sec = (double) count / seconds;
System.out.println(String.format("libmdbx put:%d %.2f MB %.2f MB/S %.2f OP/S %.2f Sec", count, totalWrites, speed, op_sec, seconds));

startTime = System.nanoTime();

Transaction txRead = env.createTransaction(true);
try {
for (Map.Entry<byte[], byte[]> entry : map.entrySet()) {
db.get(txRead, entry.getKey());
}
} finally {
txRead.commit();
txRead.close();
}

double totalReads = (4 + (long) emptyBytes.length * (long) count) / 1024.0 / 1024.0; // Всего сырых данных

endTime = System.nanoTime();
duration = endTime - startTime;
seconds = duration / 1_000_000_000.0;
speed = totalReads / seconds;
op_sec = (double) count / seconds;
System.out.println(String.format("libmdbx get:%d %.2f MB %.2f MB/S %.2f OP/S %.2f Sec", count, totalReads, speed, op_sec, seconds));

db.close();
} finally {
// Make sure you close the env to avoid resource leaks.
env.close();
}
}
}
19:14
Все это работает через нативную библиотеку в java, c libmdbx пробую работать 2 дня, до этого проверил примерно 40 различных БД в поисках самого быстрого варианта, если кто-то объяснит мне значения каждого из параметров env.setGeometry(-1, -1, 1024 * GB, 2 * GB, 1, KB); буду очень благодарен
Л(
19:34
Леонид Юрьев (Leonid Yuriev)
1. setMapSize() не нужен, это legacy метод полностью перекрываемый возможностями setGeometry(), а в API libmdbx он оставлен для большей совместимости/похожести на LMDB.

2. Набор параметров setGeometry() нельзя назвать безобидным:
size_lower = -1 // не изменять (оставить текущее значение и/или по-умолчанию = близко к нулю),
size_now = -1 // не изменять (оставить текущее значение и/или по-умолчанию, = 2 Мб)
size_upper = 1024 Гб // на слабых машинах может не хватить PTE для резервирования такого объема
growth_step = 2 Гб // как только в БД закончатся исходные 2 Мб будет сделано приращение сражу аж на 2 Гб и не факт что на диске будет столько места
shrink_threshold = 1 // в данном случае "1" означает установить минимально возможное значение (скорее всего это будет 4Кб), но когда shrink_thresholdме меньше growth_step могут быть огромные пенальти из-за частого изменения туда-сюда размера файла БД
pagesize = 1K // при установке отличного от системного размера (не по-умолчанию) нужно хорошо понимать цель и последствия.

Вся информация есть в описании функции mdbx_env_set_geometry().

// упс, поправил ссылку на описание функции
ЖБ
22:22
Жосслен Бомон
In reply to this message
Спасибо за подробное объяснение, Единственная проблема с которой я сейчас сталкиваюсь это недостаточный размер size_upper, как только размер базы достагает этого значения я сразу же получаю ошибку put failed,rc:MAP_FULL
22:31
Но если я устанавливаю заведомо большое значение например 1ТБ то проблем нет, но не будет ли это означать, что максимальный размер базы не сможет привышать этот придел?
7 August 2023
H. T. invited H. T.
9 August 2023
Deleted invited Deleted Account
10 August 2023
𝚂 invited 𝚂
Л(
13:24
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Просьба = пожалуйста, не задавайте вопросов, ответы на которые прямо даны в документации. Если-же что-то не понятно в документации, то уточняйте какая именно формулировка вызывает сомнения (с тем, чтобы можно было откорректировать/дополнить и устранять недостатки/неточности/неоднозначности).
👍
Ε
i
AV
4
11 August 2023
Maxim Eryomenko invited Maxim Eryomenko
16 August 2023
researcher invited researcher
Л(
21:50
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Приветствую!
Я сейчас в отпуске, занят другой деятельностью и основную часть времени off-line.
Поэтому отвечу по-быстрому/предварительно и по-русски.
👍
AS
Л(
22:29
Леонид Юрьев (Leonid Yuriev)
Исторически MDBX наследует от LMDB поддержку размещения больших/длинных записей в "overflow"-страницах.
Эта фича есть и работает, но это побочный функционал с архитектурными недостатками.

Такие большие/длинные записи размещаются в смежных/последовательных страницах БД, что позволяет использовать при чтении прямой доступ к памяти.
Однако, возникает проблема, если требует несколько (2, 3 и более) страниц - нужно искать такие последовательности в GC/freelist.
Проблема возникает из-за того, что b-tree имеет тенденцию к стохастическому/случайному использованию страниц.

Поэтому в большой БД вероятность образование последовательностей свободных страниц имеет обратно-экспоненциальное распределение.
Например, вероятность пары свободных страниц может быть 1/1000, а трех подряд уже 1/1000000, и т.д.

При каждом размещении длинной записи необходимо либо найти требуемое кол-во смежных/последовательных свободных страниц, либо увеличить размер файла БД при отсутствии.
Соответственно движок сначала ищет в GC (читает все записи и объединяет списки в памяти), а при отсутствии увеличивает файл БД.
При более-менее стабильном/циклическом сценарии работы размер БД приходит в равновесие, так как с увеличением размера БД, в среднем увеличивается и кол-во страниц в GC, а следовательно увеличивается и вероятность образования последовательностей.

Однако, в целом такой поиск достаточно затратный процесс, несмотря на ряд улучшений уже сделанных в MDBX:
- дроблением больших списков страниц при помещении в GC, что позволяет избежать поиск/выделение последовательностей страниц для нужд GC;
- акселерация поиска последовательностей с использованием AVX/NEON;
- эвристики и опция MDBX_opt_rp_augment_limit.

С этими и другими доработками актуальная версия MDBX в проблемных ситуация тратит на порядки меньше времени.
Например, в описанной вами ситуации, вместо 10 секунд для работы старых версий могут потребоваться десятки минут, а у LMDB могут уйти часы.

Тем не менее, полностью решить проблему можно либо отказавшись от хранения больших/длинных записей в последовательных страницах, либо существенно переработать механизм хранения и поиска страниц в GC.
У меня есть идеи по этому поводу, но я не хочу давать каких-либо обещаний.
Скорее всего часть подобного функционала будет доступна к концу года, но сделано это будет для надлежащей работы репликации для проектов Positive Technologies.

До готовности этого кода можно дать только один совет = не использовать длинные записи требующие несколько страниц БД, в особенности требующих 3-х и более страниц.
👍
AS
СМ
3
SC
VS
22:32
Victor Smirnov
Как более частный совет, можно пользоваться multikey, разбивая большие value на блоки размера 4K-header_size.
Л(
22:39
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Да, но только разбирать на блоки размером возвращаемым mdbx_env_get_valsize4page_max().
👍
VS
17 August 2023
Л(
12:04
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Да, вы правильно понимаете что такой опции нет.
Это означало-бы уничтожение версии/снимка данных существовавшего на момент старта транзакции.
Соответственно, при этом теряется возможность откатить транзакцию (в том числе при ошибке записи на диск) и не может быть конкурирующих читателей (так как нет консистентного снимка данных для чтения).

В больших "серверных" движках БД такой подход используется за счет использования undo-log, когда страницы с данными обновляются/переиспользуется inplace, а возможность отката обеспечивается дозаписью информации в undo-log.
В MDBX такой подход не оправдан, так как поддержка undo-log без серверного процесса БД крайне дорога и слабо уменьшает write amplification.

В MDBX, как в LMDB, освобождающие страницы с данными зафиксированными в предыдущих транзакциях всегда поступают в GC, а используется повторно только когда нет активных читателей использующих соответствующие снимки данных.
👍
AS
VS
СМ
VS
17:07
Victor Smirnov
In reply to this message
Как сказал Леонид, это поломает транзакционность. Во всех дизайнах хранилища, основанных на CoW, высвобождение памяти отложенное (в следующей транзакции или через одну), поэтому приходится резервировать дополнительную память в размере x2-x3 от размера транзакции. Это — неизбежная плата за конкурентность и, в случае LMDB/MDBX, — за wait-free. В LSM всё, в целом, еще хуже, так как придется ждать следующей запланированной компактификации хранилища, а не следующей транзакции. Учитывая, что все новые движки БД сейчас либо LSM, либо CoW, то выбирать будет особо не из чего.

Отказоустойчивое управление внешней памятью — задача алгоритмически сложная, и прям вот "красивых" (чтобы всё, сразу, по-честному и бесплатно) решений тут нет. Приходится выбирать, и дополнительное резервирование пространства — это вполне приемлемая плата за "всё хорошее".
👍
LP
AS
Л(
4
18 August 2023
Нормал Арч invited Нормал Арч
1 September 2023
Deleted invited Deleted Account
2 September 2023
MI
20:16
Marin Ivanov
bot
👍
Л(
Л(
20:18
Леонид Юрьев (Leonid Yuriev)
In reply to this message
да, был...
👍
MI
M
20:55
Mark
Hey. I haven't noticed any commits to libmdbx for a while. Is gitflic.ru still the repo that I should be pulling from?

Or have things just been stable?
Л(
21:07
Леонид Юрьев (Leonid Yuriev)
In reply to this message
The latest release and the current master branch are in very good stable state now.
and I have a lot of other things to do, a commits will be when I have time for that.

The brief roadmap for 23' autumn:
- finalization of DB check API inside library;
- rework GC and page recycling/reclaiming (a huge challenge which is required for stable replication in a next libfpta version for Positive Technologies);
- a set of API extensions and minor improvements, etc.

So the repo at git flow is still the origin.
M
21:11
Mark
Спасибо. Thanks for the info!
☭k
Л(
12 September 2023
Kirk Du invited Kirk Du
18 September 2023
AS
10:08
Alex Sharov
Hi.
At 8Kb pagesize - GC does store 2042 page-id's per record: which is 2042*8bytes=16kb - overflow pages in GC.
Garbage Collection
Pagesize: 8192
Tree depth: 2
Branch pages: 1
Leaf pages: 7
Overflow pages: 1884
Entries: 1886
Transaction 1375, 2042 pages, maxspan 187
Transaction 1376, 2042 pages, maxspan 161
Transaction 1377, 2042 pages, maxspan 436
Transaction 1378, 2042 pages, maxspan 1462
...
Л(
10:19
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Hi, Alex.

MDBX uses 32-bit page numbers.
So it is the`2042 * 4_bytes = ` ...
AS
10:21
Alex Sharov
In reply to this message
mdbx_stat -ef | grep -v 2042
Garbage Collection
Pagesize: 8192
Tree depth: 2
Branch pages: 1
Leaf pages: 7
Overflow pages: 1884
Entries: 1886
Transaction 1373, 1503 pages, maxspan 144
Transaction 1374, 1502 pages, maxspan 80
Transaction 2673, 118 pages, maxspan 31
Transaction 3258, 485 pages, maxspan 10
10:21
mdbx_stat reporting overflow pages
Л(
10:22
Леонид Юрьев (Leonid Yuriev)
In reply to this message
An overflow pages could be the 1-page sized, such "overflow pages" not a problem at all.
10:27
Please try mdbx_chk from the devel branch.
It contains a draft of new reporting features, including a lite Zipf-like diagram of overflow pages length distribution, etc.
AS
11:06
Alex Sharov
will do
21 September 2023
СМ
14:07
Сергей Мирянов
коллеги, всем привет.
подскажите, пожалуйста, в буфер data полученный от mdbx_get/mdbx_cursor_get можно писать?

в документации вроде написано что можно.
но я под Windows стабильно получаю Access Violation.

транзакция пишущая
Л(
16:24
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Нет, только читать.
Если в документации есть информация, которую можно трактовать именно так, то это ошибка (укажите где, я поправлю).

Писать можно только по адресам возвращаемым из mdbx_put(MDBX_RESERVE) и только до следующей модифицирующей операции.
На уровне хакинга еще можно обновить/поправить данные (!не ключ!) без изменения размера, если они находятся в грязной/dirty-странице, см. mdbx_is_dirty().
👍
СМ
СМ
16:33
Сергей Мирянов
In reply to this message
ясно, спасибо.

вот это (https://libmdbx.dqdkfa.ru/group__c__crud.html#ga64b0d190e0740025a1858de4086e5860)
For values returned in a read-only transaction any modification attempts will cause a SIGSEGV.
Values returned from the database are valid only until a subsequent update operation, or the end of the transaction.

я трактовал так, что можно в пишущей транзакции писать
VS
19:48
Victor Smirnov
Я бы тоже так трактовал.
22 September 2023
Кац invited Lev
Л(
11:54
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Хорошо, подумаю как переформулироввать.
Конкретные предложения приветствуются.
NT
12:00
Nodir Temirkhodjaev
Просто убрать "read-only"?
Станет " For values returned in a transaction any modification attempts will cause a SIGSEGV."
23 September 2023
𝓜𝓲𝓬𝓱𝓪𝓮𝓵 invited 𝓜𝓲𝓬𝓱𝓪𝓮𝓵
25 September 2023
Dan (StarkWare) invited Dan (StarkWare)
27 September 2023
00:27
Deleted Account
Lets say i have keys:
abca
abcb
abcd

And lets also say, i only know the start of the keys, 'abc'

Is there a way, to get all keys from a db, that start with 'abc'?
VS
00:34
Victor Smirnov
In reply to this message
mdbx_cursor_get()

Find the first value and then scroll the cursor for others.
00:35
Deleted Account
In reply to this message
You mean walking through all keys, ok, i want to avoid that
VS
00:38
Victor Smirnov
Ah, I see. You need to specify the key comparison function that will allow you to find the smallest key greater or equals with the given one. Not sure if default comparison function supports it out of the box, but the DB does.
00:38
Deleted Account
I see, thanks 🙏, i will look into it
01:24
Deleted Account
Seems like mdbx_cursor_get() with MDBX_SET_RANGE as op comes close to what i need with the default key_cmp function.
AL
10:29
Andrea Lanfranchi
@janbiedermann if you're using C++ bindings (mdbx.h++) then cursor.lower_bound is your friend.

std::string prefix{"abc"};
auto data{cursor.lower_bound(prefix /* need to cast to slice */ , false)};
while (data and data.key.starts_with(prefix)) {
// [your code here]
data = cursor.to_next(/*throw_notfound=*/false);
}

This sample is for NON dup_sorted subdb.
For dup sorted is pretty much the same with slightly different semantic.
10:33
Deleted Account
In reply to this message
Thanks 🙏, i am using C, but i will look how cursor.lower_bound is implemented, maybe i can take something from there.
18:50
Deleted Account
key_lowerbound = MDBX_SET_RANGE 😜 in mdbx.h++
28 September 2023
Dvir H invited Dvir H
30 September 2023
04:21
Deleted Account
When i use a MDBX_DUPSORT db, will it still store each duplicate key or will it store the key once and use references for duplicates?
04:23
or will it just add values to the existing key?
04:24
I am concerned about space usage, when using long keys with very short values (just a int)
VS
04:30
Victor Smirnov
AFAIK, for dups there is a separate custom b-tree to store values. But the caveat is that if always store values in a separate b-tree, it may introduce significant overhead (partially filled blocks wasting space). So there may be some threshold when separate b-tree is starting being used to store values. When Leonid wakes up, he will explain better)
04:30
Sorry for misinformation if this is not correct.
04:33
Succinct "flat" b-tree (without subtrees) supporting dups without extra key overhead would be pretty complex design.
04:40
Deleted Account
I see, thanks 🙏
Л(
06:32
Леонид Юрьев (Leonid Yuriev)
In reply to this message
a "dupsort" values stored in a most appropriate way:
- keys with single value are stored in usual b-tree;
- keys with a few values are stored in node with nested in-place sub-page, so an overhead is the size of headed of such pseudo-page;
- for other cases (i.e. keys with many values) a node with separate nested b-tree is used.

So, there are always a single key instance for duplicates, and overhead is insignificant/small.
AS
08:36
Alex Sharov
In reply to this message
It storing key once. “int values” - it’s likely an “inverted index” - and it’s most common use-case for DUPSORT.
11:17
Deleted Account
Thanks 🙏, fantastic
A
15:33
Alain
In reply to this message
Sorry for being late to the discussion. The library that you are referencing is from us and we do use it in production. We have never set anything to 1TB, normally we start with 2GB. If you have specific issues using the Java library and its configuration, please chime in and I'll try to help, but it mostly translates directly to native settings and so the official documentation is the authoritative source.
👍
Л(
1 October 2023
DH
13:28
Dvir H
Hi. I would appreciate to get a more detailed explanation of the MDBX_opt_rp_augment_limit option. Especially this sentence from the documentation: "On the other hand, too small threshold will lead to unreasonable database growth, or/and to the inability of put long values."
I understand how it may cause big db, but not how "inability of put long values".
2 October 2023
AS
05:12
Alex Sharov
In reply to this message
Multi-page values “put” - need find sequence of such free pages in free-list. Bigger sequence -> harder to find -> higher chance to reach “augment limit” and don’t re-use free space.
👍
DH
Dmitriy invited Dmitriy
D
16:17
Dmitriy
Добрый день! Есть ли способ запустить приложение использующее lmdb/mdbx под valgrind (callgrind) ? У меня почему-то с этой опцией --tool=callgrind приложение не открывает файл с базой, без valgrind всё работает нормально.
16:20
In reply to this message
sudo valgrind --tool=callgrind -v --allow-mismatched-debuginfo=yes --trace-children=yes --log-file='/tmp/prof/vg_%p.dat' --xml-file='/tmp/prof/vg_%p.xml' ./app --foreground

строка запуска под valgrind
Л(
16:45
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Если "не открывает", то возвращается какая-то конкретная ошибка. А что именно происходит и почему - скорее всего вам придется выяснять самостоятельно.

Valgrind используется при тестировании libmdbx. В GNUmakefile есть соответствующие цели (см. make help), а для CMake предусмотрена опция MDBX_USE_VALGRIND (т.е. -D MDBX_USE_VALGRIND=ON при запуске CMake).
D
16:56
Dmitriy
In reply to this message
Кода ошибки нету (под strace проверял), я предполагаю, что открывается нечто вроде пустого файла или читается как пустой файл, а дальше обрабатывается некорректно. Точнее не могу пока сказать, т.к. приложение большое и написано не мной.

В mdbx как я понял используется memcheck только, но не callgrind.
16:58
Возможно, callgrind, как-то влияет на приложение, которое использует mmap
Л(
17:48
Леонид Юрьев (Leonid Yuriev)
Если libmdbx что-то не сможет, то вернет код ошибки.
Другое дело, что код приложения может но доводить этот код до пользователя и при этом не использовать какое-либо логирование.
Тогда понять в чем дело может быть очень сложно.
Может быть вообще дело в приложении.

Попробуйте собрать тест библиотеки и запустить его вместо приложения.
Если тест не запустится, то буду смотреть в чем дело.
3 October 2023
Л(
07:39
Леонид Юрьев (Leonid Yuriev)
In reply to this message
На всякий - для сборки и запуска тестов достаточно make test в директории с исходниками libmdbx.
👍
D
DH
12:02
Dvir H
We writing to the DB transactions with big objects (mostly one per transaction with a size over 4*page_size). When we do so we see a long write time for the big object itself (mdbx_put), and this is the main writing time in the transaction.
I understand this is probably because of the need to find a sequence of pages in the GC table (if this kind of sequence even exists), and we actually see work_rsteps: 1663.
We also see that the commit of the transaction takes a lot of time, and I don't understand why.
I added a commit latency of one of such transactions.

MDBX_commit_latency {
preparation: 0,
gc_wallclock: 289103,
audit: 0,
write: 0,
sync: 3827,
ending: 26,
whole: 292958,
gc_cputime: 288350,
gc_prof: MDBX_commit_latency__bindgen_ty_1 {
wloops: 1,
coalescences: 4,
wipes: 0,
flushes: 0,
kicks: 0,
work_counter: 6,
work_rtime_monotonic: 334906,
work_xtime_cpu: 286,
work_rsteps: 1663,
work_xpages: 2,
work_majflt: 2537,
self_counter: 0,
self_rtime_monotonic: 0,
self_xtime_cpu: 58189,
self_rsteps: 0,
self_xpages: 0,
self_majflt: 1673,
},
}
👍
p
👎
1
A
22:33
Alain
Got 2 compile errors on windows after activating MDBX_ENABLE_PROFGC in core.c, about size_t conversion to uint32_t
Line 7247 prof->majflt += majflt_after - majflt_before;
Line 11097 env->me_lck->mti_pgop_stat.gc_prof.wloops += ctx->loop;

I added some casts for now, but should probably be addressed upstream
4 October 2023
Л(
08:21
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Thank you, fixed.
Л(
08:50
Леонид Юрьев (Leonid Yuriev)
In reply to this message
1) Support storing large/long values (sized in multiples of pages) is useful extra feature of MDBX, but it is not a first class since a set of limitations and architectural decisions were inherited from LMDB.
In general is necessary either to abandon the linear form storage of long data (i.e. to use fragmentation and streaming), or to maintain a certain index of page ranges instead of individual free pages (i.e. to change the format of the database).
A number of improvements are planned for the end of this year in the interests of Positive Technologies. Therefore, presumably the behavior of the engine will improve in such situations, but I do not make any promises.

2) The gc_cpu time: 288350, work_time_monotonic: 334906 and work_majflt: 2537 shows that time was spent to handle page faults and read corresponding 2.5K of pages from disk.
So GC seems large enough to has been uncached (just unfits into available RAM) and most of time was spent to read it from disk.

3) I am too busy now for provide support, even under sponsorship.
I recommend contacting the Erigon team (@AskAlexSharov, @vorot93, @ledgerwatch), as they had substantial experience of diving into this problem.
👍
DH
DH
09:54
Dvir H
Thank you for the answer; we will try to connect those you suggested.
Specifically about the transaction I sent, I also suspected that page faults caused this, but we had another transaction a bit before what I sent; it didn't have any big objects and 1031 page faults, but its latency was much lower, so I don't understand what is different.
This is the commit latency of the commit:

MDBX_commit_latency {
preparation: 0,
gc_wallclock: 2,
audit: 0,
write: 0,
sync: 1047,
ending: 0,
whole: 1050,
gc_cputime: 0,
gc_prof: MDBX_commit_latency__bindgen_ty_1 {
wloops: 1,
coalescences: 2,
wipes: 0,
flushes: 0,
kicks: 0,
work_counter: 2,
work_rtime_monotonic: 1,
work_xtime_cpu: 131,
work_rsteps: 4,
work_xpages: 0,
work_majflt: 1025,
self_counter: 0,
self_rtime_monotonic: 0,
self_xtime_cpu: 0,
self_rsteps: 0,
self_xpages: 0,
self_majflt: 6,
},
}
Л(
10:27
Леонид Юрьев (Leonid Yuriev)
In reply to this message
The work_steps should be taken into account also, it is a GC reading steps/iterations count.

Thus work_rsteps: 1663 shows that the 1663 GC's items was loaded to satisfy a particular allocation request(s) for a few consecutive pages.

+ And in the last your example the work_rsteps: 4 and work_xpages: 0 shows that is no page sequences and only 4 GC items/loads.
10:33
Also the gc.self_majflt: 1673 for huge-latency commit shows that many page faults were during GC reading.
A
10:55
Alain
We are finding issues (somewhat like Dvir H) constantly updating large records. Most of the size and changing part of those records are list of ids, 16 bytes each which are just treated as an array right now. If we go with DUPSORT & DUPFIXED, will we have the same issue when we have thousands of values for a key or will it be managed in a more optimal fashion?
AA
11:05
Alexey Akhunov
DupSort and DupFixed do not allow very large records, the limitation is around 512 bytes as far as I remember. Generally, if you can somehow avoid having large records (by normalising your data model, for example), you should do it
11:09
when you modify very large values, you always pay a performance penalty, because the modified value will require finding blocks of consequitive pages. You first look for it in the GC list, and failing that, allocate it at the end of the DB file. That happens every time you modify such value, no matter how small is the modification. So performance degrages depending on how large the values are (the larger the worse), how large the GC list is (the longer the worse), what is the distribution of sizes of blocks of consequitive of pages in the GC list (the more diverse the worse)
A
11:28
Alain
In reply to this message
Thanks Alexey, makes sense and what I've known but now getting worse as the size of those key records grows and they are constantly getting updated. Regarding you 1st response, I thought that 4k was allocated per value. Inserting an 8 bytes value, txn_info returns a space used of 4k.
AS
13:27
Alex Sharov
In reply to this message
increase mdbx's pageSize.
bigger pageSize -> less overflow pages and less GC pages -> less maintainance costs.
A
14:00
Alain
Sounds good but what are the pitfall, especially if the data size is quite variable from <100 bytes to >100K ?
AS
14:18
Alex Sharov
In reply to this message
1 small update does 1 page CoW. bigger page -> bigger CoW -> more writes to disk. but very likely you will not notice. need to measure on your load.
same about small reads - 1 read may cause more then 1 page-fault. you also may not notice it on real workload.
some hardware (like moders mac) anyway using 16kb pagesize.
bigger pagesize may also make db less-fragmented on disk -> more FS-compression-friendly as a result.
A
14:20
Alain
Thanks make me feel better about doing it.
Rina invited Rina
5 October 2023
A
20:44
Alain
Facing an issue with a put with MDBX_MULTIPLE on a db with DUPSORT and DUPFIXED. I can see that my 1st val has a length of 16 and then on the 1st call to cursor_put_nochecklen at line 17358 it retrieves the count from my 2nd val which is 6 here. Also my key has a length of 16. Then it starts processing and if I let it go it fails with a key length of 0. This is taken from dkey which I don't understand what it does. Looking at the image, data also looks weird to me
20:46
Debug sessin
6 October 2023
Л(
09:14
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Got this, will dig
👍
A
Л(
18:35
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Please try the fix-draft branch at gitflic.ru
A
18:35
Alain
In reply to this message
Will do, thanks so much
A
20:41
Alain
Runs great now. Here a small test with 1000 transactions, each with 50 put of 6 * 16 bytes keys, where we can see that the test with MULTIPLE (grouped) is almost twice as fast (Debug build)

Starting uuidRandom_100Bytes ungrouped
Completed 1000 in 0m:14s:788ms:608400ns
Env Version:0.12 Stats:Stat{psize=4096, depth=4, branchPages=79, leafPages=10597, overflowPages=0, entries=1200001}
Info:EnvInfo [geo={lower=12288, upper=4194304000, current=2097152000, shrink=2097152000, grow=1048576000}, mapSize=4194304000, lastPgNo=10829, lastTxnId=2004, latterReaderTxnId=2004, SelfLatterReaderTxnId=2004, meta0TxnId=2002, meta0Sign=-1, meta1TxnId=2003, meta1Sign=-1, meta2TxnId=2004, meta2Sign=-1, maxReaders=114, numReaders=1, dxbPageSize=4096, sysPageSize=4096, bootId={current={x=-5294237871968987654, y=-5742039308527149499}, meta0={x=-5294237871968987654, y=-5742039308527149499}, meta1={x=-5294237871968987654, y=-5742039308527149499}, meta2={x=-5294237871968987654, y=-5742039308527149499}}, unsyncVolume=0, autosyncThreshold=0, sinceSyncSeconds16dot16=0, autosyncPeriodSeconds16dot16=0, sinceReaderCheckSeconds16dot16=1581967, mode=67108864, pgopStat={newly=12676, cow=130548, clone=0, split=10672, merge=0, spill=0, unspill=0, wops=143227, prefault=0, mincore=0, msync=0, fsync=0}]
Dbs:
primary:Stat{psize=4096, depth=3, branchPages=79, leafPages=10596, overflowPages=0, entries=1200000}

Starting uuidRandom_100Bytes grouped
Completed 1000 in 0m:7s:968ms:505900ns
Env Version:0.12 Stats:Stat{psize=4096, depth=4, branchPages=80, leafPages=10660, overflowPages=0, entries=1200001}
Info:EnvInfo [geo={lower=12288, upper=4194304000, current=2097152000, shrink=2097152000, grow=1048576000}, mapSize=4194304000, lastPgNo=10894, lastTxnId=2004, latterReaderTxnId=2004, SelfLatterReaderTxnId=2004, meta0TxnId=2002, meta0Sign=-1, meta1TxnId=2003, meta1Sign=-1, meta2TxnId=2004, meta2Sign=-1, maxReaders=114, numReaders=1, dxbPageSize=4096, sysPageSize=4096, bootId={current={x=-5294237871968987654, y=-5742039308527149499}, meta0={x=-5294237871968987654, y=-5742039308527149499}, meta1={x=-5294237871968987654, y=-5742039308527149499}, meta2={x=-5294237871968987654, y=-5742039308527149499}}, unsyncVolume=0, autosyncThreshold=0, sinceSyncSeconds16dot16=0, autosyncPeriodSeconds16dot16=0, sinceReaderCheckSeconds16dot16=1135840, mode=67108864, pgopStat={newly=12740, cow=130479, clone=0, split=10736, merge=0, spill=0, unspill=0, wops=143222, prefault=0, mincore=0, msync=0, fsync=0}]
Dbs:
primary:Stat{psize=4096, depth=3, branchPages=80, leafPages=10659, overflowPages=0, entries=1200000}
🤝
Л(
A
21:13
Alain
Dealing with 4k of value (256*16) the difference is extreme 7m:46s with single put vs 1m:37s with multiple (that is 25.6M values)
👍
Л(
7 October 2023
Л(
08:18
Леонид Юрьев (Leonid Yuriev)
In reply to this message
В целом я не понял вашего вопроса, ибо вся информация есть в документации в явных формулировках.

Значение size_upper в геометрии ограничивает размер БД.
Это именно верхний предел, который не может быть превышен при автоматическом увеличении размера БД, при достижении которого и последующей нехватке места будет возвращаться ошибка MDBX_MAP_FULL.

В любое время вы можете задать новую геометрию, в том числе с другим значением size_upper (очевидно что нельзя уменьшить размер меньше распределенного пространства, а размер страницы можно менять/задавать только до создания БД).
Однако, изменение size_upper для уже открытой/используемой БД может быть потенциально не-успешной и очень дорогой операцией, так как сопряжено с изменением размера memory-mapped региона во всех использующих БД процессах.
Соответственно, для успешного увеличения size_upper необходимо чтобы в адресных пространствах всех работающих с БД процессах был свободный регион достаточного размера, а в системе в целом было достаточно PTE (с учетом всех особенностей конкретной платформы/системы и конкретной версии ядра ОС).

Вопросы мы можете задавать всегда, а вот если есть сомнения в адекватности/корректности/оптимальности поведения библиотеки, то лучше их высказать в ближайшее время (чтобы потенциальные доработки/исправления попали в v0.12.8).
Л(
10:43
Леонид Юрьев (Leonid Yuriev)
In reply to this message
The fix is now available in the master branch at gitflic.ru
10:46
Исправление регресса put(..., MDBX_MULTIPLE) доступно в ветке master.

Релиз v0.12.8 предположительно будет через неделю, но не позже конца октября.
Весьма вероятно что одновременно с релизом v0.13.1
10:48
@Alain, please update/fix the link to libmdbx origin repo at your https://github.com/castortech/mdbxjni
👍
A
A
11:20
Alain
Forgot to mention that I got this compile warning/error reported:
core.c(17795): error C2220: the following warning is treated as an error
core.c(17795): warning C4701: potentially uninitialized local variable 'dkey' used
Л(
11:32
Леонид Юрьев (Leonid Yuriev)
In reply to this message
This was fixed already
👍
A
11:34
In reply to this message
@Alain, please fix the link, it is 404 for now.
A
11:40
Alain
In reply to this message
Had used an older one from the README page, but I like the 404 page image :)
😁
Л(
8 October 2023
Л(
10:18
Леонид Юрьев (Leonid Yuriev)
In reply to this message
На всякий для ясности - показанный фрагмент с упоминанием SIGSEGV полностью выглядит так:

The memory pointed to by the returned values is owned by the database. The caller need not dispose of the memory, and may not modify it in any way. For values returned in a read-only transaction any modification attempts will cause a SIGSEGV.
Values returned from the database are valid only until a subsequent update operation, or the end of the transaction.

В моём понимании формулировка исключает неверное трактование, но я изменю её и смещу акценты.
СМ
10:20
Сергей Мирянов
меня сбило с толку упоминание про read-only, если бы его не было - я бы ориентировался на предыдущее предложение.
Л(
10:41
Леонид Юрьев (Leonid Yuriev)
In reply to this message
 * \note The memory pointed to by the returned values is owned by the
* database. The caller MUST not dispose of the memory, and MUST not modify it
* in any way regardless in a read-only nor read-write transactions!
* For case a database opened without the \ref MDBX_WRITEMAP modification
* attempts likely will cause a `SIGSEGV`. However, when a database opened with
* the \ref MDBX_WRITEMAP or in case values returned inside read-write
* transaction are located on a "dirty" (modified and pending to commit) pages,
* such modification will silently accepted and likely will lead to DB and/or
* data corruption.

Так лучше ?
👍
СМ
СМ
10:42
Сергей Мирянов
да, так гораздо лучше
🤝
Л(
10:42
спасибо!
Л(
21:46
Леонид Юрьев (Leonid Yuriev)
В ветку master на gitflic.ru пролито несколько доработок.
Большей частью это устранение несущественных недостатков, как например устранение ругани Valgrind/memcheck в специфических ситуациях.

В текущем понимании это всё что стоит включить в следующий релиз v0.12.8, а новые фичи будут в v0.13.1 на базе ветке devel.

Просьба потестировать на предмет регрессов.
👍
СМ
9 October 2023
Л(
13:39
Леонид Юрьев (Leonid Yuriev)
Камрады, мне нужно расширить ОЗУ в сервере и унифицировать набор модулей:
- докупить 12 штук M386AAG40MMB-CVF.
- продать (сдать на комиссию) 2 штуки M393AAG40M32-CAE и 5 штук M393AAG40M3B-CYF.

С покупкой всё относительно просто, а вот с продажей нет, ибо серверные модули по 128 Гб - очень специфическая вещь.
Поэтому просьба поспрашивать знакомых заводчиков серверов - вдруг кто-то сможет выкупить мои модули или поменяться.
10 October 2023
A
19:40
Alain
What is the best cursor pattern to retrieve all values for a db with dupsort and dupfixed
I can't seem to get MDBX_GET_MULTIPLE to position on the record and so I have to do a
cursor.get(SET, key) before doing the cursor.get(GET_MULTIPLE, key, val) and then iterate with NEXT_MULTIPLE. Otherwise if I just do a cursor.get(GET_MULTIPLE, key) I get a EINVAL
Л(
22:39
Леонид Юрьев (Leonid Yuriev)
In reply to this message
This was historical behavior (inherited from LMDB), but fixed right now in the master branch.
Please check it out.
11 October 2023
Л(
10:14
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Just for clarity - now you can use cursor.get(GET_MULTIPLE, key) and will not get a EINVAL.
👍
A
10:16
But please check it in your cases.
👍
A
A
10:16
Alain
In reply to this message
When researching I also found on some OpenLDAP forum that LMDB wouldn't work with multiple if it happened to have a single value, but tested MDBX and that wasn't the case.
Л(
10:18
Леонид Юрьев (Leonid Yuriev)
In reply to this message
For case of a single value you should just get in with MDBX_SUCCESS.
I.e. following the principle of least surprise - it should just work.
10:20
So far, I don't see any reason for this not to work, or for any other behavior to be more rational/reasonable.
A
10:21
Alain
As I was saying, that works as it should work, no surprise as you say
10:21
Leonid, every time I pull from master, I still have the fork for the mdbx_txn_release_all_cursors that I had to add a little while ago, any plan to add it or a similar version?
Л(
10:22
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Yes, I will do this in the devel branch for v0.13.x.
Thanks for reminding me.
👍
A
Л(
11:22
Леонид Юрьев (Leonid Yuriev)
Выпуск v0.12.8 запланирован на 17 октября.
A
14:10
Alain
Facing a small issue in a test that I can't understand, steps:
1, Open write txn and write some data, commit
2. Open read txn, open cursor, read data, close cursor, commit
3. Open write txn, call mdbx_dbi_flags and get error MDBX_BAD_DBI happening here:
if (dbi < CORE_DBS ||
(dbi >= txn->mt_numdbs && dbi >= txn->mt_env->me_numdbs))
return false;
where dbi == 2, nt_numdbs = 2 and me_numdbs = 2;

The test originally working didn't have step 2 above, it did 1, then 3, then 2 without issues.
🤔
Л(
Л(
20:48
Леонид Юрьев (Leonid Yuriev)
In reply to this message
I will dig.
12 October 2023
Л(
10:47
Леонид Юрьев (Leonid Yuriev)
In reply to this message
1.
Did this issue reproduced without your(s) patch(es) on the clean current master branch?

2.
Please give me more details.
For instance - describe when you are creates named subDb, opens/closes dbi-handles, commit/abort/reset/renew ANY transactions.
A
11:35
Alain
In reply to this message
On 1, in this case this is not active, so is not called. on 2. let me gather more detailed info and get back to you
🤝
Л(
Л(
13:02
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Firstly we need to find out if the issue is the result of multi-thread races conditions, or is it due to a simpler bug in the code.

Next, instead of describe a scenario you cold build libmdbx with -DMDBX_DEBUG=1 then setup a logger with mdbx_setup_debug(MDBX_LOG_DEBUG, MDBX_DBG_ASSERT, your_logger).
This is preferable, because it excludes the possibility of incompleteness and/or distortion of information due to human factor.
See Logging and runtime debug.
A
13:17
Alain
yes, that is what I intended to do. logging support was added a few months ago and you may recall I was asking if debug could be changed at runtime. So I will run and capture all debug.
🤝
Л(
13 October 2023
AS
05:25
Alex Sharov
If my app has many libmdbx databases - can it touch some OS's limits. Like "virtual memory limit"? or something else? what is right way to check it?
Л(
07:19
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Наиболее вероятна нехватка ресурсов и/или превышение лимитов при выполнении mmap().
Поэтому см секцию ошибки в man mmap и далее по ссылкам, в частности getrlimit([RLIMIT_DATA, RLIMIT_DATA, RLIMIT_RSS]).

Кроме этого, при очень большой нагрузки или утечке ресурсов могут быть схожие ошибки (ENOMEM, EAGAIN, ENFILE) при других системных вызовов.
Но при этом в системе может сбоить и не работать всё что угодно, а какие-либо общие рецепты дать нельзя.
A
15:50
Aртём
Товарищи вопрос. Если я открываю курсор и фильтрую по ключу в один поток. Стоит ли запускать ещё пару курсоров конкурентно? Сократиться ли время чтения если распаралелить?
Л(
16:15
Леонид Юрьев (Leonid Yuriev)
In reply to this message
1. В пишущих транзакциях вы не должны использовать многопоточность.

2. Одну читающую транзакцию вы можете использовать из разных потоков с опцией MDBX_NOTLS, но должны обеспечить отсутствие гонок при открытии, закрытии курсоров и до-инициализации (чтения состояния named subDB из БД).

3. При использовании нескольких читающих транзакций вам следует убедиться/проверить что все транзакции видят один снимок БД (между запусками читающих транзакций не было зафиксировано ни одной пишущей).

4. Если на момент фильтрации необходимые страницы БД будут уже в ОЗУ, то узким местом может стать memory bandwidth.

5. Если же необходимы страницы БД не будут в памяти, то все обращения к ним (page faults) и подкачка с диска будут сериализованы ядром ОС. При интенсивном чтении с диска именно он будет узким местом, а увеличение кол-ва потоков только увеличит конкуренцию и дребезг.

6. Чтобы подгрузить БД в ОЗУ или даже зафиксировать все страницы используйте mdbx_env_warmup(), но если БД больше размера доступной/свободной памяти то толку не будет.

Как-то так.
👍
СМ
A
A
16:21
Aртём
In reply to this message
Спасибо
VS
17:48
Victor Smirnov
In reply to this message
> 3. При использовании нескольких читающих транзакций вам следует убедиться/проверить что все транзакции видят один снимок БД...

Для этого в самом API базы хорошо бы иметь метод, возвращающий массив из N объектов для транзакций, гарантированно смотрящих в один и тот же снэпшот. И танцы с NOTLS не понадобятся)
Л(
18:05
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Это очень редкий сценарий, если не эксклюзивный.

Создание "массива транзакций" предполагает их регистрацию в таблице читателей, что без MDBX_NOTLS требует передачу в параметрах массива thread-id в IPC-формате.
Далее, при такой параллельности в реальности нужно:
- адекватным образом поделить объем работ между потоками;
- объединить результаты, так чтобы само это объединение не стало узким местом;
- на NUMA разместить все потоки и БД на одной ноде, либо учитывать пенальти при планировании.

В результате в API появиться еще одна функция, даже описание которой никто не будет читать ;)
👍
A
VS
18:12
Victor Smirnov
In reply to this message
Поэтому я и сказал, что "хорошо бы", а не "нужно". MDBX всё равно не аналитическая БД из-за того, что "долгие" читатели блокируют высвобождение памяти. То, что b-trees в MDBX не позволяют разделить зоны сканирования равномерно между потоками — это тут уже второстепенный фактор.
14 October 2023
AS
03:51
Alex Sharov
In reply to this message
Есть другой подход:
- принять что чтения из БД - быстрые (mdbx быстрый)
- а медленные только page faults
В этой схеме можно читать однопоточно - но наплодить пул воркеров которые в оне будут делать ручной “read-ahead” - просто щупать странички заранее и выбрасывать все прочитанное - чтобы главный поток не натыкался на page-faults.

Такой подход сильно упрощает все - нет конкурентности и локов, можно ускорять как читающие так и пишущие транзакции, можно подстраиваться под «high-latency high-throughput” диски (просто больше воркеров в фоне).
cc: Artjom
VS
04:29
Victor Smirnov
In reply to this message
Это всё можно делать — для частных случаев. Основная идея тут в том, что MDBX изначально не аналитическая, на долгие запросы (условно, часы) не рассчитана, поэтому и режимы MPP для неё не интересны. Предлагать и поддерживать такой API (а это будет далеко не единственный вызов), проекту нет смысла.
AS
04:37
Alex Sharov
Вопрос: сработает ли msync - при коммите пустой транзакции?

Использую это чтобы вынести msync из критической секции:
appMutex.Lock()
begin(NoSync)+updates+commit()
appMutex.Unlock()
begin()+no_updates+commit()

Вроде работает.
Л(
06:21
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Для этого следует использовать mdbx_txn_begin(MDBX_TXN_NOSYNC) и mdbx_env_sync_ex() после коммита (строго до старта следующей транзакции) в отдельном асинхронном воркере
15 October 2023
12:28
Deleted Account
I am trying to understand a performance issue i see. Saving many small items, one by one, each in its own transaction, is expected to be slower, of course as saving them all in one transaction. However, i also see a huge amount of io happening, when saving small items, that makes me wonder.

If i touch multiple dbi with small items, does it have to save a page per dbi?

If i save a few bytes with keys of ~ 512 bytes to, lets say 5 dbs, each one item, in one transaction, how many pages are written to disk?
12:36
For that scenario i get ~1000txn/s
but disk writes >300M/s

But its not saving even close to that much data.
Why is that happening?
12:49
To answer my question in part, sure, its about the number of pages written, changing page size in set geometry reduces io to 25 M/s, thats better, but still way to much
12:49
How can i see, how many pages are written and how can i reduce that number?
13:00
with a page size of 4k, and just above 1000txn/s and a io of just over 80M, i can conclude that around 15 pages are written per txn. does that look reasonable?
13:01
And that is on Windows, in case that matters
13:15
So in the btree, each node is one page, when i save, possibly 15 nodes are touched, so thats the amount of pages to save, is it?
Л(
14:08
Леонид Юрьев (Leonid Yuriev)
Basically, MDBX is B-Tree + MVCC, ACID, but without WAL,

When you update a leaf page you should have some multi-version structure inside such page, or just apply updates to a copy of a page and leaves original unmodified - this is requirement of MVCC..
MDBX uses the second approach which known as Shadow paging.

So, any update of a leaf page applies to a copy, while original stay untouched and available for readers.
However, this requires update a reference to modifies leaf page and so on. Thus any single update strike copy-on-write a chain of page from the target leaf to the root of b-tree.
14:15
When you update one or a few neighbors items on a single leaf page, you will get WAF (write amplification factor) in pages equal to the height of b-tree.

When you update/insert/append a lot of consecutive ordered items you will get WAF near to amount of pages required for these new items.

However, if you touch/modify just one key-value pair on each leaf page then you get WAF equal to number of used DB pages.
14:31
Deleted Account
Alright, that explains it, thanks 🙏
17 October 2023
Л(
20:04
Леонид Юрьев (Leonid Yuriev)
Выпуск libmdbx 0.12.8 (Владимир Уткин).
24 commits, 18 files changed, 624 insertions(+), 94 deletions(-)

Стабилизирующий выпуск с исправлением обнаруженных ошибок и устранением недочетов, в день 100-летия со дня рождения выдающегося советского и российского ученого и конструктора Влади́мира Фёдоровича У́ткина.

- Устранение регресса/ошибки в пути обработки put(MDBX_MULTIPLE) при пакетном/оптовом
помещении в БД множественных значений одного ключа (aka multi-value или dupsort).
Проявление проблемы зависит от компилятора и опций оптимизации/кодогенерации, но с большой вероятностью возвращется
ошибка MDBX_BAD_VALSIZE (-30781), а в отладочных сборках срабатывает проверка cASSERT(mc, !"Invalid key-size").
Сценарии приводящие к другим проявлениям на данный момент не известны.

- Реализована перезапись в mdbx_put(MDBX_CURRENT) всех текущих мульти-значений ключа
при отсутствии флага MDBX_NOOVERWRITE. Ранее в такой ситуации возвращалась ошибка MDBX_EMULTIVAL.
В текущем понимании новое поведение более удобно и не создаёт проблем совместимости с ранее написанным кодом.

- Добавлена возможность использовать mdbx_cursor_get(MDBX_GET_MULTIPLE) без предварительной установки
курсора, совмещая операцию пакетного получения данных с позиционированием курсора на передаваемый ключ.

- Микрооптимизация и рефакторинг cursor_put_nochecklen() в продолжение исправления
регресса/ошибки в пути обработки put(MDBX_MULTIPLE).

- Уточнение формулировок в описании API, в том числе пояснений о SIGSEGV
и недопустимости прямого изменения данных.

https://gitflic.ru/project/erthink/libmdbx/release/df006830-3ec1-4983-87c0-eab7b0e12d9c
👍
VS
СМ
18 October 2023
A
17:18
Alain
Is there any way to rename a database ?
AL
19:30
Andrea Lanfranchi
In reply to this message
If I'm not mistaken you can by updating the corresponding record on subdb 0 (aka MAIN_DBI)
A
19:31
Alain
In reply to this message
Makes a lot of sense, will look into it
AL
19:32
Andrea Lanfranchi
But be sure you've never opened the db you want to rename in the session as the tuple dbi/Name is cached
👍
A
19 October 2023
Л(
19:39
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Please run mdbx_chk -vvvv [path-to-db] from the devel branch on gitflic, and show the log.
19:41
@shekhirin, пардон, чуть не забыл - запускать mdbx_chk следует при остановке всей другой активности с БД.
👍
AS
Л(
20:22
Леонид Юрьев (Leonid Yuriev)
Насколько я помню, в Reth в БД помещаются достаточно большие/длинные записи, и таких записей много.
Этого достаточно, происходит примерно вот что:

В b-tree порядок/паттерн использования страниц стремиться к случайному (точнее говоря стохастическому, т.е. псевдо-случайному) - это одно из поведенческих свойств b-tree.

Для длинных/больших записей MDBX (как и LMDB) выделяет последовательные страницы.

Когда вы добавляете длинную запись (например 64К), то требуется найти последовательность из 64/4=16 свободных страниц.

Из-за стохастического свойства b-tree вероятность образования последовательностей свободных убывает экспоненциально.
Из-за этого 16-ти свободных страниц подряд в БД может не быть.

В этом случае MDBX сначала прочитает всю GC, в надежде найти последовательность там, а при отсутствии выделит дополнительные страницы и при необходимости увеличит размер БД.

Если при этом объем полезных данных в БД не увеличивается, но происходит приращение БД из-за отсутствия последовательностей свободных страниц нужной длины, то всё добавленное место "выпадает в осадок" в GC/freelist.

При этом последующие итерации становятся еще дороже, так как GC/freelist становится больше, что увеличивает затраты на поиск.
Предполагаю что это вы и наблюдаете.



Не выполнять поиск в GC/freelist при этом не стоит, ибо так БД будет расти бесконечно.
Тем не менее, это поведение можно контролировать посредством MDBX_opt_rp_augment_limit.

В целом это "старая история", обусловленная архитектурным недостатком унаследованным от LMDB = размещение больших/длинных значений поддерживается, то это как-бы дополнительная фича, которая не масштабируется.

В MDBX сделано достаточно много, чтобы уменьшить последствия - в некоторых сценариях MDBX работает в 100-1000-10000 раз быстрее LMDB.
Но полностью проблема пока не решена.

В Erigon достаточно долго боролись с этим, в том числе вместе со мной.
Собственно поэтому в последние 2-3 года в MDBX появилось несколько доработок, как например BigFoot (MDBX_ENABLE_BIGFOOT) и профилирование GC (MDBX_ENABLE_PROFGC).
👍
AS
VS
20:26
Этой зимой я планирую сделать несколько доработок в интересах Positive Technologies.
В том числе сделать несколько улучшений в GC для устранения ряда технических проблем в механизме репликации.
Думаю станет лучше, но каких-либо обещаний я давать не хочу.
👍
AS
Л(
20:44
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Да, такое может быть.
Как-раз будет видно что у вас там.

Но может быть и баг в devel-ветке. Например, зацикливание или deadlock.
Диском-то шуршит ?
Что в iotop ?
20:59
In reply to this message
Нет, не так.

В MDBX используется подход MVCC + page-shadowing, т.е. какие-либо изменения вносятся только в копии страниц, а оригиналы остаются как-есть и при коммите помещаются в GC/freelist.
cv. https://t.me/libmdbx/5042
👍
AS
21:01
In reply to this message
Самый плохой сценарий - это обновление длинных записей, особенно если с увеличением длины.

Подозреваю что у вас именно так сейчас.
21:11
In reply to this message
Не что занимают две и более overflow-страницы.
Одинарные overflow-страницы безобидны.
👍
AS
20 October 2023
AS
04:46
Alex Sharov
In reply to this message
расскажите подробнее почему "одинарные overflow-страницы" безобидны? (одинарные НЕ-overflow-страницы более безобидны или нет?)
VS
04:47
Victor Smirnov
In reply to this message
Потому, что они имеют тот же самый размер, что и обычные страницы, и они в пуле свободных страниц есть всегда.
AS
04:48
Alex Sharov
In reply to this message
одинарная overflow - это 2 страницы? 1основная+1overflow?
VS
04:48
Victor Smirnov
А вот вероятность того, что мы сможем выделить страницу большего размера, убывает по экспоненте с ростом размера.
04:50
In reply to this message
НСЯЗ, overflow — это просто обычная страница, которая уже не в основном дереве, а лежит "сбоку".
AS
04:51
Alex Sharov
In reply to this message
значит когда мы говорим "одинарная overflow" - мы имеем ввиду сценарий поиска последовательности из 2-х страниц в GC?
VS
04:51
Victor Smirnov
Т.е. если значение не помещается в b-tree на месте, оно помещается в отдельную страницу такого размера, в который это значение влезет.
04:56
In reply to this message
overflow страница не обязана идти сразу за основной. она может быть где угодно.
AS
04:56
Alex Sharov
tnx
Л(
13:24
Леонид Юрьев (Leonid Yuriev)
In reply to this message
 - Traversal GC/freeDB by txn#9433109...
key-value kind: ordinal-key => single-value, flags: integerkey (0x08)
fixed key-size 8
value length density: 1028=1, 3856=1, 3912=1, 4008=1, 4012=3, 4040=1, 4068=1, 4072=7, 4076=26097
= summary: 27486 records, 219888 key's bytes, 109205188 data's bytes, 0 problem(s)
span(s) 20367133 (distribution: single=16891119, 2=2062770, 3=688287, 4=296058, 5=159755, 6=92324, 7=57033, 8=36649, 9=1, 28=1)

- Traversal sub-database(s) by txn#9433109...
...
- Processing subDB Bytecodes...
key-value kind: usual-key => single-value, flags: none (0x00)
entries 1108390, sequence 0, last modification txn#9433038, root #338673981
b-tree depth 4, pages: branch 1537, leaf 98457, large 2433176
key length density: 32=1108390
value length density: 658=1, 24057=8, 24058=327, 24959=4, 24960=16755, 26921=17, 26922=95, 27697=31, 27699=217
= summary: 1108390 records, 35468480 key's bytes, 8509981698 data's bytes, 0 problem(s)
...
- Processing subDB Receipts...
key-value kind: usual-key => single-value, flags: none (0x00)
entries 2121956758, sequence 0, last modification txn#9433099, root #424205156
b-tree depth 5, pages: branch 280044, leaf 62728929, large 1623999
key length density: 8=2121956758
value length density: 4=16379346, 5=870498285, 92=26315, 93=595117, 94=13031551, 95=14305703, 96=21757211, 392=1, 327731=1
= summary: 2121956758 records, 16975654064 key's bytes, 212381140532 data's bytes, 0 problem(s)
...
- Processing subDB Transactions...
key-value kind: usual-key => single-value, flags: none (0x00)
entries 2121956758, sequence 0, last modification txn#9433099, root #424197661
b-tree depth 5, pages: branch 444503, leaf 99567689, large 33601667
key length density: 8=2121956758
value length density: 100=488780, 101=2987364, 102=10133879, 103=27958721, 104=54312670, 105=130154461, 106=150096896, 231=1, 230384=1
= summary: 2121956758 records, 16975654064 key's bytes, 478622445555 data's bytes, 0 problem(s)

У вас в БД немало длинных записей, в частности почти все данные в Bytecodes требуют по 6-7 страниц.
Но при этом есть под одному подозрительно длинному элементу в Receipts и в Transactions, которые почти в 1000 раз длиннее остальных.
Каждой из этих причин достаточно, особенно если такие записи буду обновляться.
👍
AS
13:26
Советую убедиться что записи размером 327731 и 230384 байт не являются результатом бага.
👍
AS
Л(
13:49
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Нет.
Добавляемые в GC/freelist страницы идентифицируются только номерами, т.е. смешаны/неподкрашены.
AS
18:54
Alex Sharov
In reply to this message
👍
AS
21 October 2023
Л(
08:33
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Видео может быть полезным, насколько помню там достаточно подробное и наглядное объяснение всех основных концепций.
Но там ситуация многолетней давности, поэтому есть смысл ознакомиться с Restrictions & Caveats в документации.
👍
AS
08:46
In reply to this message
MDBX - достаточно эластичная и прочная сова, поэтому не порвется (как минимум совсем не сразу) если натягивать на такой глобус ;)
Однако, если таких блокчейн-транзакций будет более-менее существенное количество, то тормоза будут жуткие.

Варианта решения проблемы примерно три:
1. Добавить в MDBX поддержку потокового режима для больших/длинных значений. При этом внутри хранятся небольшие куски, а снаружи к ним stream-интерфейс
Это существенная работа.

2. Самостоятельно реализовать нарезку длинных данных используя multi-value.

3. Хранить большие/длинные данные вне MDBX.
👍
AS
СМ
09:11
Сергей Мирянов
In reply to this message
Мы пошли третьим путем
Л(
09:45
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Да, я курсе.
@ledgerwatch рассказывал о намерениях еще года три назад, если не ошибаюсь.

Но если думать о технологической красоте, то я бы предложил такое (на основе MithrilDB:
- длинные и константные данные хранятся в БД;
- длинные данные хранятся в потоковом режиме;
- БД из разных файлов, в том числе на разных дисках и с NUPS (non-uniform page size);
- дерево Меркла с хорошим хешем (я бы скомбинировал Стрибог и SipHash, но можно и на AES-NI);
- синхронизация всей БД, отдельных named subDb или веток b-tree - быстрее чем rsync (rolling checksum) и с возможностью p2p-сетей (аналогично тянем по хешам, только страницы, а не блоки).
Skandesh invited Skandesh
VS
10:31
Victor Smirnov
In reply to this message
В MithrilDB механизм высвобождения страниц памяти будет такой же, как в MDBX?
Л(
10:53
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Формат хранилища будет другой, а переработка истории нелинейной.
Поэтому не будет проблемы долгих читающих транзакций.

Но возможно что-то из этого будет и в MDBX (при сохранении формата БД).
VS
10:55
Victor Smirnov
In reply to this message
Вот про переработку истории — самое интересное)) Публиковать будете?)
Л(
10:56
Леонид Юрьев (Leonid Yuriev)
In reply to this message
С публикацией Mithril-а пока всё туманно, лучше я не буду давать каких-либо обещаний.
VS
10:58
Victor Smirnov
Я понимаю)
A
13:04
Alain
In reply to this message
As Alexey correctly pointed out to one of my previous question, went with option 2 to normalise some of our data that were long arrays. Just wished that array -> multiple values would have been more natural. Since the values are sorted, had to add the index in the array as the first part of the values and this is not ideal when data shifts in the array (which for us is rare). Also have to use a fixed size prefix even if 2 bytes would cover 99.5% of all cases (even 1 byte would cover 75%)
22 October 2023
A
15:12
Alain
Back on this renaming database question. Tried it and it's not working
Since the key seems to be the name, I am trying to make a copy, and I can list it but can't open it
try (Transaction tx = env.createWriteTransaction()) {
byte[] dbData = env.getMainDb().get(tx, bytes("foo"));
env.getMainDb().put(tx, bytes("foobar"), dbData, 0);
}
listDbs("After adding foobar");
after();
openEnv();

db = env.openDatabase("foobar"); <==fails here with MDBX_INCOMPATIBLE
byte[] data = db.get(bytes("foo"));

errors in core.c at:
/* make sure this is actually a table */
MDBX_node *node = page_node(couple.outer.mc_pg[couple.outer.mc_top],
couple.outer.mc_ki[couple.outer.mc_top]);
if (unlikely((node_flags(node) & (F_DUPDATA | F_SUBDATA)) != F_SUBDATA)) {
rc = MDBX_INCOMPATIBLE;
goto bailout;
}

I suspect the I'm missing F_SUBDATA that seems to "control/identify" main db content but unsure. Anyway what would be the solution here.
24 October 2023
A
17:02
Alain
Experiencing some performance issues with dupsort/dupfixed database on long transaction (single process running). Running a pure iterative test doesn't show this issue. Both db have same config. On test I put 16 bytes UUID and 6 * 16 bytes values 1M times in 1m40s (both 1 commit at end).
Now on the application itself, it starts by bootstrapping the schemas and does 51k put of 16 UUID over many transaction. Those and a bunch of other put to other dbs are taking 3m30. The previous version that used to store the values as a chunk ran in 1m30. The difference here is bit large, but reasonable in that we have to index all the values since they are sorted.
Now for the long transaction that creates the initial project. This is going from 5secs to 42m. Here we have 45k put with a total of 151k values. What is happening is that the time for the put keeps increasing linearly:
0-1100 : 0-1ms, 1k: > 1ms, 3k > 5ms, 5k > 10ms, 10k > 20ms, 20k > 40ms, 30k > 60ms, 45k ~90ms.

And the set of operations for each are mostly all similar to this:
06:49:42.568 o.e.e.c.s.h.internal.db.HgChangeSet - Applying op:Type:DEFINE2,sgHdl:defLiveHandle(uuidHandle(740f700e-973d-479f-adde-6e0bd1ce166d)),atomHdl:uuidHandle(a2aea030-3471-4bcf-a55d-fe9a886bdb62),tpHdl:uuidHandle(a6f5bf1d-5cdc-4735-b64c-361242125e75)
06:49:42.618 o.h.s.mdbx.MdbxStorageImplementation - Store2:uuidHandle(371afef0-eebf-4e99-917f-0874172ca75b), kl:16, dl:80, cnt:4, ms:50
06:49:42.619 o.h.s.mdbx.MdbxStorageImplementation - Added incidence link for uuidHandle(644f4292-1593-4f8a-9614-c5642afa8b3b)(false), to:uuidHandle(371afef0-eebf-4e99-917f-0874172ca75b), type:uuidHandle(83fab6dd-3f00-4d72-9039-ab4134878c26)
06:49:42.619 o.h.s.mdbx.MdbxStorageImplementation - Added incidence link for uuidHandle(9c35cd11-e740-4975-827e-37504aecee84)(false), to:uuidHandle(371afef0-eebf-4e99-917f-0874172ca75b), type:uuidHandle(83fab6dd-3f00-4d72-9039-ab4134878c26)
06:49:42.619 o.h.s.mdbx.MdbxStorageImplementation - PrimitivePut hdl:uuidHandle(56e845d1-8b21-4162-9f90-dd72c1d09602), data:16,14
06:49:42.619 o.h.s.mdbx.MdbxStorageImplementation - Added branch data for uuidHandle(56e845d1-8b21-4162-9f90-dd72c1d09602)(false), data:[-128, 0, 1, -117, 97, 72, 27, 90, 55, 26, -2, -16, -18, -65, 78, -103, -111, 127, 8, 116, 23, 44, -89, 91, 0]
06:49:42.654 o.h.s.mdbx.MdbxStorageImplementation - Store2:uuidHandle(30a9f389-8f76-4fe1-9ddb-4ce35f7ab397), kl:16, dl:60, cnt:3, ms:35
06:49:42.691 o.h.s.mdbx.MdbxStorageImplementation - Store2:uuidHandle(a2aea030-3471-4bcf-a55d-fe9a886bdb62), kl:16, dl:60, cnt:3, ms:36
06:49:42.692 o.h.s.mdbx.MdbxStorageImplementation - Added incidence link for uuidHandle(30a9f389-8f76-4fe1-9ddb-4ce35f7ab397)(false), to:uuidHandle(a2aea030-3471-4bcf-a55d-fe9a886bdb62), type:uuidHandle(fc8854fb-0d34-11da-ac60-932fd7ea200d)

where we can see the Store2 operations that are at issue. Note that the incidence links are 16 bytes key and 16 bytes values to a dupsort db as well and those are always sub zero ms. The primitive Db is a regular db and again no issue for those can be observed.

Obviously most transaction are very small and this here is a one time transaction, but we have some cases of long transaction in production when we have to import batches of data. We can't go from 5-10secs to 45m.
I was using put multiple and switch to put one by one and there was no real/big difference. Any ideas?
Л(
18:25
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Are you sure using a non-debug build of the library?
i.e. with the -DNDEBUG compiler option?
A
19:32
Alain
yes,
mdbx_stat version 0.12.7.23
- source: v0.12.7-23-g27e0ebed-dirty 2023-10-11T03:23:54-04:00, commit 27e0ebed15e52e6abf37dceaf2306a3542de9320, tree 36b29944d1fe564c2f11a5750f40cd12b0b1e1a3
- anchor: a6d5fb9ffabda5eaf29830fbcc191be145ec1843cc0c6ac970187b5b10472306_v0_12_7_23_g27e0ebed_dirty
- build: 2023-10-22T20:46:37Z for x64-Windows-Release by MSVC-19.37.32825.0
- flags: /Ox /Ob2 /DNDEBUG /DWIN32 /D_WINDOWS /GR /EHsc /Gy /Zc:__cplusplus /W4 /utf-8 /bigobj /WX /GL LIBMDBX_EXPORTS MDBX_BUILD_SHARED_LIBRARY=1
- options: MDBX_DEBUG=0 MDBX_WORDBITS=64 BYTE_ORDER=LITTLE_ENDIAN MDBX_ENABLE_BIGFOOT=1 MDBX_ENV_CHECKPID=AUTO=0 MDBX_TXN_CHECKOWNER=1 MDBX_64BIT_ATOMIC=AUTO=1 MDBX_64BIT_CAS=AUTO=1 MDBX_TRUST_RTC=AUTO=1 MDBX_AVOID_MSYNC=1 MDBX_ENABLE_REFUND=1 MDBX_ENABLE_MADVISE=1 MDBX_ENABLE_MINCORE=0 MDBX_ENABLE_PGOP_STAT=1 MDBX_ENABLE_PROFGC=0 _GNU_SOURCE=NO MDBX_WITHOUT_MSVC_CRT=0 MDBX_BUILD_SHARED_LIBRARY=0 MDBX_MANUAL_MODULE_HANDLER=0 WINVER=0x0A00 MDBX_CACHELINE_SIZE=64 MDBX_CPU_WRITEBACK_INCOHERENT=0 MDBX_MMAP_INCOHERENT_CPU_CACHE=0 MDBX_MMAP_INCOHERENT_FILE_WRITE=0 MDBX_UNALIGNED_OK=8 MDBX_PNL_ASCENDING=0
25 October 2023
Л(
10:33
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Ok, I need more information:

0) Pleass build mdbx_chk from the current devel branch on gitflic, i.e. just clone the repo and execute the make inside.
This version provide more detail of information.

1) Run mdbx_chk -vvvvvv before long transaction and show output.

2) Temporary add -DMDBX_ENABLE_PROFGC=1 to enable GC/FreeDB profiling.

3) Show the mi_pgop_stat given from mdbx_env_info_ex() before long transaction, before it commit, and immediately after the commit.

4) Use mdbx_txn_commit_ex() to commit long transaction and then show entire returned MDBX_commit_latency.

+update:
5) Run mdbx_chk -vvvvvv after long transaction and show output.
A
10:47
Alain
In reply to this message
will do, any special make step on Windows
Л(
18:55
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Я уже пояснял выше - у вас в БД достаточно длинных записей.
Кроме этого, переработка GC останавливается при длительной транзакции чтения, в том числе при работе mdbx_chk, mdbx_copy, mdbx_dump, mdbx_stat.
Проще говоря, БД будет расти, если один процесс будет коммитеть, а другой удерживать переработку GC читающей транзакицией.
см. Restrictions & Caveats

А транзакции с большим количеством retired-страниц (включая большие удаления) не являются проблемой начиная с v0.12.1, если конечно включен BigFoot - по умолчанию он включен на 64-битных сборках, но не на 32-битных.

Если есть намерение отслеживать ситуацию, то советую пойти путем Erigon:
- включить профилирование GC (посредством -DMDBX_ENABLE_PROFGC=1)
- сделать dashboard (на графане, etc) куда выводить MDBX_commit_latency (из mdbx_commit_ex()) и MDBX_envinfo (из mdbx_env_info_ex()).
🙏
AS
18:57
На всякий - в MDBX есть эксклюзивный режим работы с БД.
Может помочь как "защита от дурака", на случай если пользователь что-то будет делать с БД из другого процесса (и этим остановит переработку GC).
18:59
Ну и напомню, что этой зимой я планирую доработать GC, чтобы обеспечить нелинейную переработку.
Это требуется для надлежащей работы репликации, где также возникают длительные читающие транзакции.
Л(
20:27
Леонид Юрьев (Leonid Yuriev)
@shekhirin, еще такой немаловажный момент.
Вы сейчас (пока еще) обладаете свежим, "не замыленым" взглядом.
Поэтому можете видеть недостатки и слабые места в документации.
Соответственно, у вас есть возможность внести свой вклад - постарайтесь сделать пару-тройку коммитов в документацию (любой текст можно найти через git grep).
👍
AS
26 October 2023
A
13:31
Alain
In reply to this message
Results
13:32
I hope that it doesn't destroy the final check, but after the long transaction, there is a flood of about 1k txn that just wraps the process.
13:34
btw the db that is taking the increasing time is "datadb"
Л(
15:30
Леонид Юрьев (Leonid Yuriev)
Alain, in general I didn't see any oddities.
So I have no idea yet what in MDBX could be the reason for such a slowdown (

I think you should first make sure that the slowdown is due to MDBX, and not because of swap-in DB pages from disk, antivirus or other external reasons.
It's hard for me to give advice, since I haven't used Windows for a long time.
However, perhaps you can use utilities like Performance Monitor, Process Explorer/Process Hacker, ProcDump, etc.

It is also advisable to clarify whether the issue is related only to Windows, or manifests itself on Linux.

If you assume that the reason is MDB, then I advise you to try Intel Performance Analyzer (aka VTune).
Once it was possible to get it for free for open-source development.

I will be able to deal with this issue only when there is a reproducible testcase, or the cause will be highlighted by VTune.
In other words, you should try VTune, and if the situation does not clear up, then prepare a test case that produces the issue.
In particular, you can consider the option when you provide a ready-made executable file using MDBX as a DLL.
A
15:31
Alain
In reply to this message
Quick info, we did run it yesterday on Linux and the time was very long, but about 15% less.
Л(
15:33
Леонид Юрьев (Leonid Yuriev)
In reply to this message
15% is an acceptable/expected lag for Windows.
A
15:34
Alain
But still close to 40 minutes :(
Л(
15:35
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Then it would be convenient for me to investigate and fix the issue on Linux, and then check on Windows.
15:36
In reply to this message
Yes, it's too slow.
A
15:37
Alain
yes, the issue is present as much on Linux, the 15% being the normal "spread". I'm thinking how to make it reproduceable, since the uni test harness doesn't generate this issue.
🤝
Л(
27 October 2023
A
21:44
Alain
I've started to just turn on debugging and capturing the output to try to narrow down where time is being spent and then maybe add some additional code to capture key latency timing. Any hint or idea on what I should look for. These 2 are at about 800-900 in the iteration sequence, when normally I start to see some increase. I also compared with one of the first and it would highlight the same areas
21:45
28 October 2023
Л(
10:55
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Oh, basically this is wrong way, either you wanna be a mdbx-hacker (but in this case you should spend at least 333 hours to study source code firstly).
Nonetheless, if you wanna collect and analyze latency over call-graph you should take look to gmon which call-arcs count and execution time.

So I strongly recommend that you focus on providing a test case reproducing the issue.
It is likely that less than 100 lines of C code will be enough for this.
Л(
11:26
Леонид Юрьев (Leonid Yuriev)
Another option - it is perhaps the issue can be reproduced using utilities mdbx_dump and then mdbx_load.
11:31
In other words, if we assume that there is a performance regression in MDBX working with duplicates, then the pattern/distribution of the corresponding key-value pairs will be enough for me.
A
13:29
Alain
I wasn't trying to become an mdbx hacker here, far from that. But my hope was that this exercise would provide you with insight to help narrow down where the issue is happening. We can already see that the elapsed time happens around a couple of places. My goal was to use this and add other debug and keep narrowing down so that the issue or where it is happening becomes obvious to you. Since the time increases linearly, it does tend to indicate that there could be some form of scan that is happening and grows over the lifetime of the transaction. I realized that the unit test values pattern was different so I adjusted it and introduced also varying number of values in the range that is usually seen and still the unit test upserting in a single db doesn't show any issue.
29 October 2023
Alexey Butenko invited Alexey Butenko
Л(
12:30
Леонид Юрьев (Leonid Yuriev)
В ветке master только-что поправлена глупая ошибка приводящая к не-включению использования mincore() и следом к некоторой деградации производительности в режиме MDBX_WRITEMAP для больших БД.
https://gitflic.ru/project/erthink/libmdbx/commit/ed8c7ead4e0f7afc50491d34918eae85dff64b86

@AskAlexSharov, для вас это актуально. Рекомендую попробовать эту версию.
👍
AS
AS
12:45
Alex Sharov
оо, заначка.
Л(
12:52
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Именно для этого исправления нет, так как:
1) проблему легко обойти явно включив -D MDBX_ENABLE_MINCORE=1;
2) у Alain есть жалобы на деградацию производительности и до новых релизов нужно понять в чем там дело.
👍
AS
A
13:01
Alain
In reply to this message
With my increased debugging, I found the issue and it is not in mdbx code. It is caused by some result set based cursors that would not release their cursors and this is what would keep growing to what was finally > 750k cursors. It happened in 2 locations in cursor_put_nochecklen like here:
for (m2 = mc->mc_txn->mt_cursors[mc->mc_dbi]; m2; m2 = m2->mc_next) {
cursCnt++;
if (m2 == mc || m2->mc_snum < mc->mc_snum)
continue;
if (!(m2->mc_flags & C_INITIALIZED))
continue;
if (m2->mc_pg[i] == mp) {
So I'm looking into it, as this has been like this from the BDB original implementation that predates me and it has not shown known issues in 10 years, and here with the switch to using lots of dup sort (and dup fixed) instead of standard records, it is showing clearly that this was very wrong.
Л(
13:13
Леонид Юрьев (Leonid Yuriev)
In reply to this message
This is the "cursor tracking" code.
It adjusts the state/position of all cursors of the same subDB/dbi when some page was changed.
So if you have a lot of cursors then this will cause performance trouble(s), no option.
A
13:19
Alain
Option is for us to close those cursors ASAP (or undind/renew). That is what I'm going through, but have to review hundreds of places where many of the calls are not closing their result sets which in turn doesn't close the cursor. This was designed to track and release at commit time. Fine I guess for small txn but not for long ones. This is why I really felt that since the unit test with a single db could not reproduce the issue, it had to be something else in our usage pattern. One thing that I had to do and that might be nice to control, is that when applying debug, you automatically get assert, audit and jitter. In a case like mine, I really was thrown off by this overhead. Just the debug imposed a lot less overhead in terms of execution time and for me provided much more accurate timing in order to find what was causing the time amplification.
👍
Л(
A
17:06
Alain
Now the only result I see: Function:cursor_put_nochecklen, line:17914, msg:after subroot with 1 cursors [80400] 👍, time to go back to a release version. Thanks for standing by, appreciate it.
A
19:00
Alain
Well the long txn time went from 40m to just below 58secs, and this is not reusing cursors and we know there are 750k of them, so should be able to still shave off some time.
👍
VS
Л(
30 October 2023
A
20:52
Alain
In reply to this message
Actually found a conditional breakpoint was left on, it is a fantastic: 0m:3s:568ms
👍
Л(
31 October 2023
A
01:38
Alain
Asked before but have yet to be able to rename db. Was able to get it done with dump and load but very long process to changes some names. Any other way ? I have tried renaming in db 0 but seems to be "protected". Any utility or suggestion to even have a CLI program mdbx_rename or similar would still be a solution.
Artiom invited Artiom
AV
05:16
Artem Vorotnikov
In reply to this message
Is this a one-time operation? It really is easier to just read, load and drop the old table.
Л(
08:40
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Ok, I will extend API.
👍
A
Л(
22:43
Леонид Юрьев (Leonid Yuriev)
In reply to this message
В формулировке issue я бы несколько сместил акцент - проблема не в большом GC/freelist, а в возникновении ситуаций когда помещенные туда страницы использовать невозможно или дорого:
- остановке переработки GC из-за долгих читающих транзакций.
- потребность в последовательностях свободных страниц для помещения длинных значений.

Советы примерно следующие:

1) Посмотрите все упоминания "Handle-Slow-Readers" в mdbx.h,
2) Контролируйте размер транзакции, см. MDBX_txn_info.
3) Про увеличение размера страницы (не меньше системной) и дробление больших записей и вынос константных данных вы уже сами написали, но также стоит подумать/попробовать вынести длинные данные в отдельную БД - как вариант mdbx с большим размером страницы (32 или 64 Кб).
👍
AS
1 November 2023
Л(
11:30
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Please check out mdbx_dbi_rename() in the devel branch.
СМ
11:36
Сергей Мирянов
In reply to this message
MDBX_val data = {&txn->mt_dbs[dbi], sizeof(MDBX_db)};

я правильно понимаю, что всё внутреннее состояние предыдущей db скопируется и seq будет в корректном состоянии?
Л(
11:45
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Да.
Семантика переименования и сторонние эффекты соответствуют подходу POSIX: меняется только имя, а открытые хендлы остаются валидными.
👍
СМ
A
11:48
Alain
In reply to this message
will do and many thanks
🤝
Л(
Л(
13:01
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Please test it.
Also take look to mdbx_txn_release_all_cursors() and mdbx_cursor_unbind().