1 November 2023
A
13:02
Alain
I did look at (and used during my long txn testing) mdbx_txn_release_all_cursors() but have just pass 0 for the new argument
👍
Л(
Л(
13:51
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Расширение ОЗУ завершено.
Осталось продать (сдать на комиссию) 2 штуки M393AAG40M32-CAE (128 Гб, 3200 Mhz, CL26) и 5 штук M393AAG40M3B-CYF (128 Гб, 2933 MHZ, CL24).
Объявления на авито №1, №2.
13:51
In reply to this message
A
14:20
Alain
I am adjusting the API for the new version and I'm seeing mdbx_txn_lock/unlock. Curious what those do since we have a single write txn
Л(
14:25
Леонид Юрьев (Leonid Yuriev)
In reply to this message
It needed to bundle a few DBs, extra hacking, etc.
👍
A
A
14:31
Alain
is mdbx_env_chk stable and I should include it now or any chances it doesn't make it?
Л(
14:41
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Seems no bug(s) there, but it not frozen.
14:44
In reply to this message
In the coming days I will document this API and then you will be able to enable it.

Nonetheless, you need to try using this new API right now to request/suggest any improvements.
So please check it out.
A
14:45
Alain
ok will do
🤝
Л(
A
15:55
Alain
In reply to this message
1st simple tests are a go:
Adding record foo -> bar to foo db
Original DB list
bar
foo
primary
secondary
After renaming DB foo to foobar
bar
foobar
primary
secondary
Retrieving foo record from foobar db
found:bar
After re-open
bar
foobar
primary
secondary
found:bar
15:56
later today will move to a real use case
👍
Л(
A
18:52
Alain
Is it possible on a DUPSORT & DUPFIXED to do a cursor_get with MDBX_GET_BOTH_RANGE & MDBX_GET_MULTIPLE. I have a db that is a candidate for DUPFIXED and it is currently DUPSORT only and using MDBX_GET_BOTH_RANGE as it keys in on the key and a value prefix.
Deleted invited Deleted Account
2 November 2023
Л(
13:59
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Hmm, what confused you? Why aren't you sure?

DUPFIXED is a sub-mode of DUPSORT, and MDBX_GET_MULTIPLE is intended for DUPFIXED exactly.
So, yes, of course.
A
14:04
Alain
In reply to this message
What I'm confused about is to combine both MDBX_GET_BOTH_RANGE & MDBX_GET_MULTIPLE on the first cursor get, and then continue as usual with MDBX_NEXT_MULTIPLE
Л(
15:04
Леонид Юрьев (Leonid Yuriev)
In reply to this message
The MDBX_GET_MULTIPLE just does:
1) If the cursor not positioned yet then sets it to the given key.
2) Stores the address and size of dupfixed-values chunk from the current cusor position to the end of current paged.
3) Sets cursor position to the end - 1 of the current page. This -1 is the pre-compensation for subsequent MDBX_NEXT_DUP which performed internally during MDBX_NEXT_MULTIPLE.

The MDBX_NEXT_MULTIPLE just does:
1) Performs MDBX_NEXT_DUP from end - 1 of previous MDBX_GET_MULTIPLE's cursor position. The MDBX_NEXT_DUP used here since it is the simple way for move to the next sibling dupfixed-page without bloating the code.
2) and 3) the same as MDBX_GET_MULTIPLE.

Thus you could set cursor to key and first duplicate by MDBX_GET_BOTH_RANGE, then check/expect taken data value, and then use MDBX_NEXT_MULTIPLE up on end of a range.
👍
A
5 November 2023
Deleted invited Deleted Account
6 November 2023
11:20
Deleted Account
How to correctly use mdbx with multiple threaded processes?

I read the docs, and i tried to follow instructions, but when closing envs i frequently, not always, get a error then.

The process creation is out of my control and happens every when and then, also process may get killed any time, its a forking web server.

So after process gets created, i try to open the env (its always the same db on the filesystem), which works ok. Docs say, i am supposed to close the env of the parent, but that does not seem to work right or i am "holding" it wrong.

What i understand from the docs, i open the env in the parent, parent forks, in the new process i close the env of the parent and repoen the env.

Now at this stage, is the env in the parent still valid? Am i supposed to continue using the env within the parent, the one, that has been closed in the child?

Anyway, closing the parent env in the child returns MDBX_PANIC, is that supposed to be this way?
Л(
11:24
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Briefly: you should re-open in the child process, but not in the parent.
11:25
Deleted Account
thats what i am trying to do
11:27
now after the second fork, client cannot close parent env and reports MDBX_EBADSIGN, probably double free
11:27
second fork of the original parent
11:29
oh, i just saw there may be a bug on my side, the parent closing the env ... gimme a minute or 20
11:39
ok, that was the bug, found it 🙏
11:47
so that works, but child still reports MDBX_PANIC when closing the parent env, that just seems to be that way, and that is on macOS Intel this time
Л(
11:50
Леонид Юрьев (Leonid Yuriev)
In general, a parent cannot give/pass the environment to child processes.
Because the sharing mechanism/algorithm relies on the PID of a process.

Thus, it doesn't matter what happens in the parent process.
It is quite possible that there is no point in opening the database in the parent process if it does not use the DB itself.

However, the parent process can warm up the database using mdbx_env_warmup().
Also, theoretically, it is possible to implement an API function for quick/lightweight resurrection of the environment in child processes, but so far there has been no such need.
11:52
Deleted Account
the parent uses it too, its not a real parent - child design, just a always "fork the parent", parent is a worker just the same as the children, it just serves as the reference for forking too
Л(
11:57
Леонид Юрьев (Leonid Yuriev)
In reply to this message
I assume that there are bugs, since this scenario has been smoke-tested only and some time ago.

Moreover there is a dependency on the platform/system.
For example, closing in the child process a shared mutexes and/or other IPC synchronization objects, which was opened in the parent, may result in errors returning from the corresponding system functions.
Since such IPC-objects likely become invalid after a fork.
libmdbx will just panic in such cases.
👍
Л(
13:14
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Basically, the "fork-to-work" design/approach is a bad for libmdbx, at least for now:

1) Most of IPC-objects may became invalid immediately after the fork.
This is depends both on a platform/system and IPC-locking/sync used by libmdbx (see MDBX_LOCKING build option).

2) A lot of difficult-to-reproduce problems (aka heisenbugs) are possible due to the use of signal-unsafe functions in libmdbx, such as madvise(), etc.

3) In multithreaded scenarios, there may be both resource leaks and double-release errors, since threads and thread-local-storage contexts (with it own destructors) are not cloned during fork, but all other API objects (transactions, cursors, etc.) will be cloned.

Therefore, I don't want to do environment recovery support after fork() unless absolutely necessary, offering the close-after-fork-and-then-open option instead.
Nonetheless, bug reports and patches for close-after-fork case are welcome.
7 November 2023
17:25
Deleted Account
In reply to this message
Yeah, close after fork is fine, no problem with that approach. However, that approach doesn't seem to work. On Linux now i get a immediate segfault when attempting to close the parents env in the child. Ill look into it.
8 November 2023
Л(
00:02
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Ok, will fix this.
9 November 2023
Icy Icy invited Icy Icy
10 November 2023
Л(
02:30
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Please check out the mdbx_env_resurrect_after_fork() from the devel branch at gitflic.
07:52
Deleted Account
In reply to this message
🙏
That works perfect! But i get problems with a dupsort code branch, but maybe thats related to some other 0.13 changes
07:53
Thank you very much!
08:16
Deleted Account
In reply to this message
But i get: Assertion failed: (rthc_pending.weak == 0), function rthc_afterfork, file mdbx, line 30013.
on FreeBSD
Л(
12:49
Леонид Юрьев (Leonid Yuriev)
In reply to this message
I've already fix that.
However, the tests are passed on Linux only for now.
14:06
Deleted Account
In reply to this message
No, not the test, its the application running on FreeBSD, forking, then showing this message, and then eventually workers crash
14:13
In reply to this message
oh, i see, git history changed, ill try again
14:22
In reply to this message
Yesss! Wonderful! 🙏 Works perfect!
11 November 2023
Нэш Джонсон invited Нэш Джонсон
Л(
13:42
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Сейчас тесты проходят, в том числе на MacOS
🙏
?
Eddy Lee invited Eddy Lee
16 November 2023
Л(
00:07
Леонид Юрьев (Leonid Yuriev)
In reply to this message
В libmdbx есть zero cost auto-compactification - это когда в процессе переработки GC прилегающие к концу БД страницы выводятся из оборота, а БД становится меньше.

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

А при включении LIFO цикл оборота страниц становится минимальным, а всё что оказывается в [djcnt GC (с мbнинимальными значениями ключей) может остаться там навсегда (до явной компактификации при копировании БД).
С другой стороны, в сценариях с большим темпом мелких транзакций и наличии battery-backed write-back cache включение LIFO может существенно увеличить производительность по-записи, так как повышается вероятность оседания iop-осв в кеша контроллера.

Больше LIFO ни на что непосредственно не влияет.
👍
AS
Л(
09:36
Леонид Юрьев (Leonid Yuriev)
In reply to this message
По этому логу что-либо сказать сложно, но в актуальных версиях update_gc() не должна выделять последовательности overflow-страниц (только по одной).
Поэтому выглядит так, как-будто эта версия без BigFoot-фичи, либо она выключена посредством -DMDBX_ENABLE_BIGFOOT=0.
👍
AS
17 November 2023
Hrithik Adhikari invited Hrithik Adhikari
HA
20:45
Hrithik Adhikari
I am using a library known as isar which is built over mdbx for flutter, I am getting an error which I think is coming from the core lib (libmdbx). Can anyone help me with why that error might be happening. I only get MdbxError (11) : Try again
Л(
21:03
Леонид Юрьев (Leonid Yuriev)
In reply to this message
It is EAGAIN (Resource temporarily unavailable) from a system call.

To find out at what operation the system returns this error, you need to enable debug logging via mdbx_setup_debug().
Look in the Isar's docs for how to do this, or ask the author to add such functionality.

Alternatively, you can try using the strace utility to at least find out which system call ends with this error.
👍
HA
21 November 2023
Seva Zhidkov invited Seva Zhidkov
SZ
21:02
Seva Zhidkov
Вопрос для понимания работы GC с длинными значениями без LIFO, в частности функции page_alloc_slowpath. Я уже посмотрел на документацию здесь, некоторые ответы в этом чате, тред здесь.

Допустим, у нас есть база данных с большим количеством записей в GC, распределенных равномерно по keyspace. (Контекст — reth).

В новой транзакции мы пробуем записать большое значение. В txn->relist нет подходящей последовательности, поэтому нужно смотреть в GC.

Как я понял, page_alloc_slowpath идет по каждой записи в GC. Для каждой записи, он добавляет набор страниц из нее в txn->relist, затем проверяет, можно ли теперь найти необходимую последовательность в txn->relist. Если все записи в GC пройдены или мы достигли augment_limit, то можно пытаться начинать брать новые страницы или расширять размер файла.

1. В какой момент страницы исчезают из GC и, соответственно, убирают необходимость итерации по ним в рамках новой записи?

2. В рамках одной транзакции, мы будем только один раз проходить по GС за счет начала итерации с txn->tw.last_reclaimed + 1. Правда ли, что если каждая запись происходит в новой транзакции, то итерация по GC будет постоянной, т.к. счетчик будет обнуляться?
Л(
21:17
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Если не сильно вдаваться в детали, то примерно так:

Функция page_alloc_slowpath() сохранит в контексте транзакции ключ/номер последней считанной из GC записи и при следующем вызове (в рамках текущей транзакции) будет считывать следующие записи.
Поэтому нет необходимости удалять записи из GC до фиксации транзакции и так было исторически (логика в этом есть - в случае отмены/прерывания транзакции не было лишних действий).

Удаление использованных записей из GC происходит при фиксации транзакции внутри функции update_gc().
К сожалению, там очень сложная и запутанная логика, так как:
- кроме удаления использованных/считанных записей из GC нужно вернуть обратно неиспользованные остатки;
- любое изменение GC потребует выделения страниц, поэтому этот процесс неизбежно логически рекурсивный;
- необходимо обеспечить не только сходимость рекурсии, но и обеспечить это за минимальное кол-во итераций;
- списки страниц могут быть огромными, поэтому требуется обеспечить их шинковку на куски помещающиеся в одну страницу;
- на всё это накладывается логика LIFO/FIFO и необходимость работы в крайних/редких случаях;

+++
В следующей транзакции чтение/поиск GC полностью повторяется, т.е. между последовательными транзакциями никакой индекс/контекст не сохраняется и не передается.
Причины две:
- транзакции могут совершать разные процессы, поэтому нужно либо сохранять этот индекс/контекст в БД (для чего требуется менять формат), либо выносить на уровень IPC (в разделяемую память, что сложно/волокитно);
- всего этого не требовалось для основных сценариев использования.
👍
HA
21:23
В планируемых этой зимой доработках как-раз будет реализован некий контекст/индекс для поиска в GC.
SZ
21:24
Seva Zhidkov
Я понял, спасибо большое.
25 November 2023
Sash Antonchenkov invited Sash Antonchenkov
S
13:37
Sash Antonchenkov
доброго времени суток.
поиск по группе ничего толком не дал, прошу помощи.
вижу в документации среди features указано "Online hot backup". подскажите как включить/настроить/использовать?
база на 2тб, хочется её бэкапить на лету, без остановки приложения.
ни документация, ни гугл, ни chatgpt не помогли.
Л(
14:15
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Приветствую.

libmdbx является движком/библиотекой, поэтому под "online hot backup" подразумевается технологическая возможность сделать через API консистентную копию БД не останавливая процессы работающие с БД и не-прерывая/не-останавливая выполнение транзакций.

Для этого в API библиотеки есть соответствующие функции, см. mdbx_env_copy() и mdbx_env_copy2fd().
Кроме этого, к библиотеке прилагаются утилиты командной строки, среди которых есть mdbx_copy.

Важно: внимательно прочтите в Long-lived read transactions о потенциальной деградации вследствие копирования БД (особенно в нагруженных сценариях и/или на медленный на носитель).
S
14:15
Sash Antonchenkov
спасибо, Леонид
AV
18:58
Artem Vorotnikov
In reply to this message
- Открываете транзакцию на чтение
- Вычитываете записи из таблиц и пишете в новую базу
- Закрываете транзакцию

ACID-гарантии MDBX гарантируют консистентность и изоляцию на время жизни каждой открытой транзакции.
👍
S
19:01
(Но помните что если транзакция на чтение живёт слишком долго и идёт активная запись в горячую БД, она может надуваться в размере из-за неосвобождения старых страниц)
AA
19:08
Alexey Akhunov
если хочется решение, которое не страдает от раздувания файла базы данных при долгой открытой транзакции, я бы посоветовал посмотреть на LVM (Linux Volume Management), так как там есть возможность очень быстро делать слепки/снимки (snapshot) на уровне файловой системы. Делаем снимок через LVM, потом копируем его командой cp, потом снимок убираем. во время копирования тоже будет раздуваться файлова система, но такое раздувание потом будет убрано. Я сам пока так не пробовал, но хочу вот как раз еще поиграться с LVM и возможно протестирую такой вариант
👍
S
👀
AV
AV
19:11
Artem Vorotnikov
In reply to this message
У btrfs, ZFS и bcachefs также есть эта опция прямо на уровне ФС.
27 November 2023
Л(
13:53
Леонид Юрьев (Leonid Yuriev)
В ветку devel на gitflic пролито расширения API с обещанным "doubtless cursor positioning API", а также сопутствующие доработки.
Просьба попробовать/посмотреть.

Суть примерно в следующем, в C API добавлены константы для mdbx_cursor_get():
  /* Doubtless cursor positioning at a specified key. */
MDBX_TO_KEY_LESSER_THAN,
MDBX_TO_KEY_LESSER_OR_EQUAL,
MDBX_TO_KEY_EQUAL,
MDBX_TO_KEY_GREATER_OR_EQUAL,
MDBX_TO_KEY_GREATER_THAN,

/* Doubtless cursor positioning at a specified key-value pair
* for dupsort/multi-value hives. */
MDBX_TO_EXACT_KEY_VALUE_LESSER_THAN,
MDBX_TO_EXACT_KEY_VALUE_LESSER_OR_EQUAL,
MDBX_TO_EXACT_KEY_VALUE_EQUAL,
MDBX_TO_EXACT_KEY_VALUE_GREATER_OR_EQUAL,
MDBX_TO_EXACT_KEY_VALUE_GREATER_THAN,

MDBX_TO_PAIR_LESSER_THAN,
MDBX_TO_PAIR_LESSER_OR_EQUAL,
MDBX_TO_PAIR_EQUAL,
MDBX_TO_PAIR_GREATER_OR_EQUAL,
MDBX_TO_PAIR_GREATER_THAN


А в С++ API добавлены элементы mdbx::cursor::move_operation:
    /* Doubtless cursor positioning at a specified key. */
key_lesser_than = MDBX_TO_KEY_LESSER_THAN,
key_lesser_or_equal = MDBX_TO_KEY_LESSER_OR_EQUAL,
key_equal = MDBX_TO_KEY_EQUAL,
key_greater_or_equal = MDBX_TO_KEY_GREATER_OR_EQUAL,
key_greater_than = MDBX_TO_KEY_GREATER_THAN,

/* Doubtless cursor positioning at a specified key-value pair
* for dupsort/multi-value hives. */
multi_exactkey_value_lesser_than = MDBX_TO_EXACT_KEY_VALUE_LESSER_THAN,
multi_exactkey_value_lesser_or_equal =
MDBX_TO_EXACT_KEY_VALUE_LESSER_OR_EQUAL,
multi_exactkey_value_equal = MDBX_TO_EXACT_KEY_VALUE_EQUAL,
multi_exactkey_value_greater_or_equal =
MDBX_TO_EXACT_KEY_VALUE_GREATER_OR_EQUAL,
multi_exactkey_value_greater = MDBX_TO_EXACT_KEY_VALUE_GREATER_THAN,

pair_lesser_than = MDBX_TO_PAIR_LESSER_THAN,
pair_lesser_or_equal = MDBX_TO_PAIR_LESSER_OR_EQUAL,
pair_equal = MDBX_TO_PAIR_EQUAL,
pair_exact = pair_equal,
pair_greater_or_equal = MDBX_TO_PAIR_GREATER_OR_EQUAL,
pair_greater_than = MDBX_TO_PAIR_GREATER_THAN

Это всё уже покрыто работающими тестами.
Просьба попробовать/посмотреть.
28 November 2023
Л(
18:23
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Да, доку нужно поправить.
Спасибо что заметили.

Но с вашим "решением" что-то не то.

При уменьшении лимита не происходит поиск в глубину GC.
Поэтому если "сверху" GC нет последовательности страниц нужной длины, то вместо переработки GC будет происходить приращение файла БД.
Соответственно, freelist/GC не уменьшается, а растет.

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

Но что у вас происходит на самом деле требует более тщательного анализа.
В лучше случае у вас какой-то баг в снятии метрик, либо есть какая-то зависимость в сценарии работы.
В худшем случае у меня какой-то баг, из-за которого GC/freelist растет при значении MDBX_opt_rp_augment_limit.
18:25
В "сухом остатке" - уменьшение MDBX_opt_rp_augment_limit точно не решает проблему роста GC/freekbst. а усугубляет её ради ускорения вставки длинных записей.

Если будут хоть какие-то сомнения, то давайте разбираться...
SZ
18:31
Seva Zhidkov
Я тоже удивился падению фрилиста, но мне сказали, что так на каждом деплое происходит. Какое-то время не слушали новые блоки — накопился беклог блоков — когда приступили к их обработке, GC работал больше.
Л(
18:40
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Эта опция была добавлена именно для этого.

Соответственно, в вашем сценарии, при уменьшении MDBX_opt_rp_augment_limit транзакции с длинными значениями стали быстрее.
Но вот уменьшение GC/freelist при этом выглядит ОЧЕНЬ подозрительным.

Грубо говоря, нужно понять от чего был рост GC/freelist до этого.
Если всё объясняет только уменьшение latency, то Ok.

Тогда можно подумать о "динамическом" MDBX_opt_rp_augment_limit.
Точнее о том, чтобы (например) искать вглубь GC, пока на это потрачено менее задаваемого лимита времени.
🔥
AS
18:42
Я могу попробовать добавить это в блийжее время в ветку devel, если обещаете сразу тестировать.
Л(
19:27
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Если размер БД упрется в предел заданный в геометрии, либо если закончится место на диске.
19:31
In reply to this message
Ну если в GC пусто и БД достигла предельного размера, то любые изменения будут невозможны.

Если же в GC что-то есть, то по-одной странице будет перерабатываться вне зависимости от MDBX_opt_rp_augment_limit.
👍
AS
29 November 2023
Л(
00:21
Леонид Юрьев (Leonid Yuriev)
In reply to this message
👍
AS
00:31
In reply to this message
@ledgerwatch и @AskAlexSharov, для Erigon это тоже может быть достаточно полезной фичей.
Vladimir Budnev invited Vladimir Budnev
Л(
23:02
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Не советую затягивать с "попробовать", а то через пару дней я займусь другим на долго.
30 November 2023
Л(
14:54
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Ну так можно же задать очень маленькое значение для MDBX_opt_rp_augment_limit и тогда таймаут MDBX_opt_gc_time_limit будет отсчитываться почти сразу.

Соответственно, вы получите нужно вам поведение.

Смысл текущей логики обработки MDBX_opt_gc_time_limit в том, чтобы не менялось поведение по-умолчанию.
👍
AS
Л(
18:01
Леонид Юрьев (Leonid Yuriev)
In reply to this message
-30791 это MDBX_DBS_FULL /* Environment maxdbs reached */.

В ветке devel сейчас новый код инициализации, проверки и импорта dbi-хендлов в транзакциях.
Соответственно, если в вашем коде нет изменений, то варианта примерно два:
- у вас какой-то баг или недочен/нелогичность при работе с dbi-хендлами, который всплыл/проявился после переделок внутри libmdbx;
- у меня баг или в регресс в упомянутом выше коде.

Вероятность бага/ошибки/рергесса есть всегда, но всё-такие новый код неплохо проверен тестами, некоторые достаточно экстремальные.
Если не найдете причину у себя, то будем разбираться...
Л(
23:17
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Нет, эта логика не менялась (внутри к значению MDBX_opt_max_db всегда добавлялось +2, и добавляется сейчас).
2 December 2023
Л(
01:01
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Мне интересно понять/узнать - позволит ли вам это что-то улучшить/ускорить не-краткосрочно.
Пишите как будет информация.
Л(
01:47
Леонид Юрьев (Leonid Yuriev)
In reply to this message
На всякий — НЕ-кратко-срочно.

Если просто установить низкий rp_augment_limit, то высока вероятность что БД будет расти нерационально/излишне быстро.
Если поставить чуть больше необходимого, то снова будут всплески задержки ( latency spikes) и опосредованный рост GC.
Поэтому угадать "золотое сечение" для rp_augment_limit в ваших сценариях (наличие комплексной нелинейной обратной связи) крайне сложно.
А ограничение по-времени, при верном использовании, позволяет перерабатывать GC максимально глубоко, но не создавая плохой обратной связи.
👍
AS
01:49
Текущая реализация примерно соответствует deadline-подходу (фактически это "жадный" алгоритм с ограничением), который является тут пожалуй единственным решением.
Поэтому интересно понаблюдать за не-тривиальной системой со сложной обратной связь и посмотреть что даст реализованный вариант авто/само-регуляции.
VS
10:40
Victor Smirnov
Не знаю, уместно ли это тут, но всё же добавлю свои 2 цента. Подобное поведение свойственно для всех дизайнов со сборщиком мусора, хотя то, что в MDBX — это не совсем сборщик мусора, это просто такой специфический алгоритм поиска свободных блоков в соответствующей структуре данных. Просто алгоритмы выделения блоков оптимизируются под сверх-быструю работу для наиболее типичного случая, за счет замедления во всех остальных (так или иначе). И обычно это именно то, что хотят пользователи.
4 December 2023
DH
20:00
Dvir H
hi. two questions about the use of db options.
1. What advantages does the MDBX_INTEGERKEY option offer? In my tests, it seems to have no impact on storage size and may even result in less dense pages.
2. I'm working with a table where both the key and value are fixed size (mapping from type A to B). Since there's no option for fixed-size keys, I'm considering creating a table with dupfix, the key will always be some value (e.g., the number 100), and the duplicate values will be the tuple (A, B). This minimizes storage usage, and I can still retrieve values similarly to how libmdbx does it, though it requires additional effort on my side. Should I anticipate any performance issues with this approach for reading or writing compared to the simple mapping A to B as regular key and value?
Л(
23:08
Леонид Юрьев (Leonid Yuriev)
In reply to this message
1. Basically MDBX_INTEGERKEY offers unsigned integer key comparison in native byte order (but without properly align) and key-length checking, no disanvantages.
Historically MDBX inherits MDBX_INTEGERKEY from LMDB, so I was just leave this option as is.
However, I assume that Howard Chu intended to implement an additional dedicate kind of BRANCH-pages like DUPFIXED to increase keys density, but for some reason did not do this.

2. DUPSORT (aka multi-value, include DUPFIXED) is a killer-feature for cases which a lot of duplicates/multi-values (for instance for secondary indices), since values are stored inside nestes b-tree and keys are stored once for a hive.
But for classic mapping case (not a multimap), you won't see a significant difference using DUPSORT option(s) or without ones.
👍
DH
11 December 2023
Л(
23:57
Леонид Юрьев (Leonid Yuriev)
v0.12.9 "Ясень-4"

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

Исправления и доработки:

- Ликвидация зависимости от ранее удаленной опции MDBX_ENABLE_PREFAULT, из-за
чего опция MDBX_ENABLE_MINCORE не включалась автоматически, что приводило
к не-активации соответствующего улучшения и не-достижению декларируемого уровня
производительности в сценариях использования в режиме MDBX_WRITEMAP.
- Исправление авто-установки MDBX_ENV_CHECKPID при отключении использования
функционала madvise() посредством опции сборки MDBX_ENABLE_MADVISE=0.
Из-за чего при поддержке системой madvise(MADV_DONTFORK) не включался контроль pid.
- Добавлена проверка переданного ключа на NULL при обработке MDBX_GET_MULTIPLE.
- Добавлена проверка номеров корневых страниц в coherency_check().
- Обеспечен const для начала и конца диапазона в аргументах mdbx_estimate_range().
- Из разрабатываемой версии перенесены не-нарушающие совместимости доработки C++ API.
- Поддержка base58 приведена в соответствии с черновиком RFC.
- Переработка/исправление to_hex() и from_hex().
- Уменьшение MDBX_opt_rp_augment_limit по умолчанию до 1/3 от текущего количества страниц в БД.

Более подробная информация в ChangeLog.

git diff' stat: 32 commits, 8 files changed, 667 insertions(+), 401 deletions(-)

https://gitflic.ru/project/erthink/libmdbx/release/1b91a3ad-3234-426b-bd35-7bab68f06d77
👍
A
N
6
1
🔥
LP
12 December 2023
Л(
10:03
Леонид Юрьев (Leonid Yuriev)
Кстати, в libmdbx в C++ API есть кодер/декор base58 и base64.

Причем реализация base58 достаточно шустрая, предположительно в 4-6 раз быстрее подавляющего большинства (но специального исследования/сравнения я не делал).
При необходимости, думаю, можно ускорить еще в пару раз.
👍
VS
13 December 2023
Л(
19:01
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Устанавливаете MinGW посредством chocolatey.
При необходимости аналогично до-устанавливаете msys2, make/ninja, git, cmake.
Этого достаточно для сборки, с поправкой на path.

GCC 8 достаточно для сборки.
Но есть какие-то проблемные версии/комбинации с libstdc++, поэтому могут быть проблемы со сборкой С++ API.

Однако, по опыту у MinGW (gcc под Windows) достаточно проблем, особенно в старых версиях.
Отдельный слой проблем - это обеспечение набора dll для runtime, начиная от собственных потрохов gcc до UWP.
Дальше традиционные для Windows проблемы совместимости при использовании разных версий runtime, в том числе single/multi threaded и т.п.
Поэтому в libmdbx предусмотрен режим сборки без MSVCRT (используется Win32 API + ntdll).



Короче, собирается и работает, но может быть достаточно проблем из-за традиций Windows и/или MSVC.
Возможно кто-то поможет, но в целом вам необходимо самостоятельно во всём этом разбираться.

Платная поддержка - обсуждаемо, но по договору хотя-бы на год, от 100 т.р. в месяц. Как-то так.
👍
16 December 2023
Л(
01:56
Леонид Юрьев (Leonid Yuriev)
Минутка философии

В масштабе земли (планеты) комфортный/обитаемый нижний слой атмосферы и чуть нижу — как поверхность/пленка мыльного пузыря.
А в масштабах самой Вселенной — Земля просто голубая точка.
Пятнышко на плёнке на точке заявляет о своей исключительности и по-знании справедливости и видения пути развития Мира.
....
(
👍
EM
К
🤯
AS
26 December 2023
DH
17:11
Dvir H
What is the format of the freeDB table (@GC from chk)? What is the key type and size? What is the value type and size?
Л(
17:19
Леонид Юрьев (Leonid Yuriev)
In reply to this message
The key is uint64_twhere is transaction id.

The value is an array of uint32_t where is a list of pages, the [0] is the number of items and rest are the page numbers. In some rare cases a few unused cells may be at the end it.
👍
DH
28 December 2023
Deleted invited Deleted Account
29 December 2023
15:51
Deleted Account
Is there new method or solution to start transtion without lock in lastest version ? (only one thread as writer, it will hold write lock and release it when process quit)
Л(
16:10
Леонид Юрьев (Leonid Yuriev)
In reply to this message
No, in general.

1. A read txn is lock free already.
Locking/waiting is required for thread (de)registration.
However read txn always acquire the latest MVCC snapshot.

2. For write txn MDBX doesn't provide option like the MDB_NOLOCK since it is so dangerous for most users.
Moreover, almost all cases using this option tend to contain errors and/or wrong assumptions, and therefore lead to data loss.
So, a long time ago, I decided to remove such feature.
It should not be an issue since mutex overhead it too small on all modern POSIX systems, and on Windows should not be considered about performance at all.

Nonetheless, you can just comment-out such locking in the source code.
16:26
Deleted Account
thanks very much for the explain. So if comment-out will work as expect if I am sure all writer in one thread.

one more question is about serialize, is mdbx plan add funtion to export full database into memory block so I can send it to S3 storeage for backup ? (or maybe compress on the fly when upload). and late restote in any where. (without stop the original app)
Л(
17:18
Леонид Юрьев (Leonid Yuriev)
In reply to this message
1. To change the source source you should should known what to do exactly, and if you does it then it is your own risk and responsibility.
This is my way to avoid supporting risky/dangerous and/or seldom-used features.

2. For now use memfd_create() + mdbx_env_copy2fd().
👍
vnvisa.top Hello invited vnvisa.top Hello
vH
18:06
vnvisa.top Hello
Hello guys, Can I remove the limitation "Maximum sub-databases: 32765"?
Л(
18:16
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Yes, but this requires change the source code.
This is not a very simple case, since you need to track the use of dbi-descriptors and change the type from uint16_t to uint32_t at least, as well as check the corresponding if/for, etc.

However, MDBX internal design is not intended for a such (relatively) huge number of named sub-DBs.
The same is true for any other high-performance key-value storage engine.

You should consider about key expansion (with prefix and/or suffix) approach instead of such a lot of subDBs.
vH
18:33
vnvisa.top Hello
You should consider about key expansion (with prefix and/or suffix) approach instead of such a lot of subDBs.

So, with this approach, how do I get all key/values pair of a prefix. Ex: get all rows which key has prefix "table1/..."
The number of rows is unknown.
30 December 2023
A
20:14
Alain
In reply to this message
We do that all the time, simply se the key to the prefix (i.e. table1/) and then cursor through the results. This can vary a bit depending on how the db was configured and you might have to check the prefix as you iterate, again depending on how the db is configured.
👍
Л(
31 December 2023
Л(
10:57
Леонид Юрьев (Leonid Yuriev)
С наступающим Новым Годом!
HNY!

https://vk.com/audio-2001101418_102101418

libmdbx v0.13.1 scheduled on 2024-01-07.
🔥
AA
VS
4
👍
3 January 2024
vH
18:16
vnvisa.top Hello
Do we need pooling techique for tnx?
4 January 2024
Л(
15:19
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Насколько помню, у @vorot93 в привязках использовалась опция MDBX_NOTLS, т.е. нет привязки к нативным потокам linux (не путать с файберами внутри Rust).
15:21
In reply to this message
Ну и да — это теч читающей транзакции.
👍
AS
15:26
В самом "пожарном" случае вы можете установить колбэк посредством mdbx_env_set_hsr() и очищать утекшие транзакции.
Это позволит меньше огорчать пользователей (течь мегабайта ОЗУ ничто в сравнении с распуханием БД на весь диск и т.п.).
👍
AS
vH
15:37
vnvisa.top Hello
Using io_arena to benchmark mdbx, the speed on ssd is slower than hdd. Do you know how to fix?
Л(
16:37
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Нет, получить указатель на экземпляр транзакции по id нельзя.

Причины примерно такие:
- через hsr видны все читающие транзакции, в том числе других процессов (вне адресного пространства в котором работает hsr);
- трекинг транзакций потребует дополнительных накладных расходов, расширения API, тестирования всего этого;

Кстати, на всякий случай, всегда обращайте внимание на pid читающей транзакции.
Ибо, например, копирование БД (посредством утилиты`mdx_copy` или mdbx_env_copyXXX()) на медленный носитель тоже будет такой залипшей читающей транзакцией.
👍
AS
16:39
In reply to this message
Ну упомянутое hsr-API именно для этого и предусмотрено.

Только в случае с другим процессом вы мало что можете сделать — фактически либо предупредить пользователя, либо kill(pid_t pid, int sig).
👍
AS
VS
17:48
Victor Smirnov
In reply to this message
Consumer-grade SSDs are slow on commits because of drive's cache flushes. You need to either sync on selective commits or use SSD together with RAID controller with battery-backed (or other non-volatile types of) cache. There are no other ways.
👍
vH
vH
18:08
vnvisa.top Hello
In reply to this message
yeah, but the same test on Iowow is faster on ssd
18:09
./ioarena -l waloff -m lazy -p /tmp -D iowow -B crud -n 200000 -v 1024 -r 2 -w 2
18:10
this is my running parameters command
VS
18:13
Victor Smirnov
try -m sync
vH
18:27
vnvisa.top Hello
this is the result when running on HDD
18:27
key-gen: using 24 bits, up to 400000 keys
doer.1: {crud}, key-space 2 and 3, key-sequence 1
doer.2: {crud}, key-space 4 and 5, key-sequence 2
time | bench rps min avg rms max vol #N
1.002 | crud: 55.781K 7.110us 32.603us 34.774us 208.260us 174.931Mbps 55.912K
2.005 | crud: 43.462K 26.750us 42.671us 42.847us 131.101us 136.298Mbps 99.483K
3.007 | crud: 42.964K 18.830us 43.209us 43.384us 118.110us 134.734Mbps 142.542K
4.009 | crud: 42.916K 31.490us 43.306us 43.483us 112.970us 134.584Mbps 185.555K
4.346 | crud: 42.867K 19.940us 43.345us 43.504us 116.100us 134.431Mbps 200.000K
complete.
18:28
this is on SSD
18:29
....still running
key-gen: using 24 bits, up to 400000 keys
doer.1: {crud}, key-space 2 and 3, key-sequence 1
doer.2: {crud}, key-space 4 and 5, key-sequence 2
time | bench rps min avg rms max vol #N
1.007 | crud: 768.898 1.318ms 2.595ms 2.620ms 8.960ms 2.411Mbps 774.000
2.011 | crud: 736.633 2.505ms 2.712ms 2.737ms 9.497ms 2.310Mbps 1.514K
3.015 | crud: 740.957 2.435ms 2.695ms 2.697ms 3.091ms 2.324Mbps 2.258K
4.024 | crud: 705.847 2.578ms 2.830ms 2.833ms 3.384ms 2.214Mbps 2.970K
5.034 | crud: 675.534 2.627ms 2.957ms 3.011ms 10.160ms 2.118Mbps 3.652K
6.036 | crud: 709.009 2.145ms 2.818ms 2.902ms 15.345ms 2.223Mbps 4.363K
7.048 | crud: 672.368 2.653ms 2.971ms 2.976ms 3.821ms 2.109Mbps 5.043K
8.051 | crud: 664.828 2.641ms 3.005ms 3.034ms 10.067ms 2.085Mbps 5.710K
9.059 | crud: 671.316 2.641ms 2.968ms 2.996ms 9.924ms 2.105Mbps 6.387K
10.067 | crud: 680.608 2.691ms 2.944ms 2.973ms 10.362ms 2.134Mbps 7.073K
11.073 | crud: 722.818 2.114ms 2.764ms 2.861ms 16.545ms 2.267Mbps 7.800K
12.085 | crud: 679.059 2.632ms 2.929ms 2.958ms 10.028ms 2.130Mbps 8.487K
13.092 | crud: 663.158 2.669ms 3.026ms 3.122ms 9.783ms 2.080Mbps 9.155K
14.100 | crud: 669.767 2.640ms 2.973ms 2.978ms 3.811ms 2.100Mbps 9.830K
15.101 | crud: 696.014 2.098ms 2.879ms 2.995ms 16.201ms 2.183Mbps 10.527K
16.105 | crud: 700.521 2.518ms 2.852ms 2.881ms 10.167ms 2.197Mbps 11.230K
17.110 | crud: 683.474 2.634ms 2.918ms 2.923ms 4.857ms 2.143Mbps 11.917K
18.112 | crud: 688.743 2.566ms 2.905ms 2.933ms 9.982ms 2.160Mbps 12.607K
19.123 | crud: 684.447 2.657ms 2.906ms 2.909ms 3.816ms 2.146Mbps 13.299K
20.125 | crud: 697.500 2.114ms 2.877ms 3.025ms 16.562ms 2.187Mbps 13.998K
21.134 | crud: 689.949 2.599ms 2.896ms 2.901ms 3.736ms 2.164Mbps 14.694K
22.137 | crud: 669.948 2.675ms 2.982ms 2.988ms 4.101ms 2.101Mbps 15.366K
23.147 | crud: 650.244 2.730ms 3.063ms 3.113ms 9.866ms 2.039Mbps 16.023K
24.159 | crud: 688.173 2.118ms 2.899ms 3.002ms 16.657ms 2.158Mbps 16.719K
25.163 | crud: 707.136 2.581ms 2.837ms 2.864ms 9.556ms 2.218Mbps 17.429K
26.175 | crud: 665.642 2.629ms 2.992ms 3.002ms 5.496ms 2.087Mbps 18.103K
27.179 | crud: 667.835 2.718ms 3.001ms 3.031ms 10.154ms 2.094Mbps 18.773K
28.182 | crud: 665.993 2.686ms 2.999ms 3.027ms 9.773ms 2.089Mbps 19.441K
29.190 | crud: 719.277 2.112ms 2.774ms 2.864ms 15.940ms 2.256Mbps 20.166K
30.198 | crud: 693.327 2.579ms 2.885ms 2.915ms 10.292ms 2.174Mbps 20.865K
31.202 | crud: 672.113 2.643ms 2.972ms 3.001ms 9.890ms 2.108Mbps 21.540K
32.212 | crud: 665.309 2.679ms 3.003ms 3.008ms 3.765ms 2.086Mbps 22.212K
33.223 | crud: 655.767 2.123ms 3.033ms 3.148ms 16.080ms 2.056Mbps 22.875K
34.227 | crud: 721.453 2.220ms 2.781ms 2.807ms 9.101ms 2.262Mbps 23.599K
35.237 | crud: 665.922 2.678ms 2.987ms 3.015ms 9.859ms 2.088Mbps 24.272K
36.243 | crud: 672.455 2.684ms 2.984ms 2.988ms 3.716ms 2.109Mbps 24.948K
37.252 | crud: 671.487 2.700ms 2.975ms 2.980ms 4.468ms 2.106Mbps 25.626K
38.261 | crud: 687.221 2.112ms 2.899ms 3.054ms 16.982ms 2.155Mbps 26.319K
39.272 | crud: 692.291 2.616ms 2.889ms 2.895ms 3.669ms 2.171Mbps 27.019K
40.277 | crud: 663.539 2.670ms 3.010ms 3.017ms 4.340ms 2.081Mbps 27.686K
41.288 | crud: 656.742 2.675ms 3.034ms 3.064ms 9.979ms 2.060Mbps 28.350K
42.292 | crud: 656.197 2.708ms 3.058ms 3.089ms 10.161ms 2.058Mbps 29.009K
Л(
18:34
Леонид Юрьев (Leonid Yuriev)
In reply to this message
1.
You should take look to the IOWOW's driver inside ioarena source code and find out what it does exactly for the waloff and lazy options.
It is likely that support for these options is not implemented and/or just works significantly differently compared to MDBX.

2.
MDBX doesn't use WAL, but IOWOW does.
This is enough so that in many scenarios, including those with lazy-flushing mode, MDBX shows higher peak WAF (write amplification factior) and so performance is limited by SSD bandwidth.

3.
When using the "lazy" option, the MDBX driver inside ioarena enables the MDBX_SAFE_NOSYNC mode.
I think you should read the description of this mode, as the details explain the higher benchmark performance on an HDD.
+ I.e. HDDs have a higher sequential write speed compared to consumer/user-class SSDs.
vH
18:39
vnvisa.top Hello
In reply to this message
2. the WAL is off with option -l waloff

3. The above results I run with -m sync follow Victor Smirnov suggestion
18:44
I used SSD of SkHynix, model PC801 with sequential write speed on this photo
VS
18:58
Victor Smirnov
SSD's numbers with -m sync look realistic, HDD's don't. What's the model of HDD?
vH
18:59
vnvisa.top Hello
In reply to this message
1TB laptop size 2.5" HGST (Hitachi) using SATA port
Л(
19:01
Леонид Юрьев (Leonid Yuriev)
In reply to this message
I think you should still find out exactly how IOWOW works when specifying the lazy and waloff options both.
Theoretically, there are only four possible options:
1. WAL is enabled.
2. No any sync/flushes to disk.
3. Some ACID (especially the Durability) are relaxed.
4. You are confused SSD and HDD mount points, etc.
VS
19:01
Victor Smirnov
Could you please check the model's capabilities? It seems that for some reason it does not honor flush commands. That may be a legitimate behavior if a drive can flush its cache on power loss events using, for example, energy from rotation.
👍
Л(
Л(
19:04
Леонид Юрьев (Leonid Yuriev)
In reply to this message
А действительно, скорее всё проще — наверное у этого "лаптопного" диска неотключаемый кеш обратной записи в расчете на аккумулятор ноутбука.
VS
19:05
Victor Smirnov
In reply to this message
Похоже на то, да. Уж очень высокие числа для HDD.
vH
19:05
vnvisa.top Hello
I am using HDD as my boot device, and mount SSD later to run benchmark. This is my HDD geometry:

fdisk -l /dev/sda
Disk /dev/sda: 931.51 GiB, 1000204886016 bytes, 1953525168 sectors
Disk model: APPLE HDD HTS541
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 4096 bytes
Disklabel type: gpt
Disk identifier: 00003398-1DDE-0000-8830-0000055A0000

Device Start End Sectors Size Type
/dev/sda1 40 409639 409600 200M EFI System
/dev/sda2 411648 1657161727 1656750080 790G Linux filesystem
/dev/sda3 1657161728 1741047807 83886080 40G Linux filesystem
/dev/sda4 1741047808 1824933887 83886080 40G Linux filesystem
/dev/sda5 1824933888 1908819967 83886080 40G Linux filesystem
/dev/sda6 1908819968 1953523711 44703744 21.3G Linux swap
Л(
19:07
Леонид Юрьев (Leonid Yuriev)
In reply to this message
VS
19:08
Victor Smirnov
The point is that HDD's numbers are unrealistically high, given that's a spinning disk.
vH
19:08
vnvisa.top Hello
this is SSD's geometry:

fdisk -l /dev/nvme0n1
Disk /dev/nvme0n1: 953.87 GiB, 1024209543168 bytes, 2000409264 sectors
Disk model: SKHynix_HFS001TEJ5X115N
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 75387D0F-3F35-4732-B318-CA14BE2862B9

Device Start End Sectors Size Type
/dev/nvme0n1p1 2048 6293503 6291456 3G EFI System
/dev/nvme0n1p2 6293504 90179583 83886080 40G Linux filesystem
/dev/nvme0n1p4 90179584 2000408575 1910228992 910.9G Microsoft basic data
VS
19:09
Victor Smirnov
Ah, yes. Leonind says that this HDD can flush it's caches on power loss events.
Л(
19:10
Леонид Юрьев (Leonid Yuriev)
I.e. your HDD has a write-back cache/queue, and it is enabled (sure).
vH
19:12
vnvisa.top Hello
In reply to this message
How can I check this?
And how to enable this feature on my SSD (if possible)?
Л(
19:14
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Basically, this is "a bug not a feature".
Your HDD configured to silently ignore flush/sync command in hope no unexpected power-off.
👍
vH
19:16
Most likely this could be disabled/enabled by BIOS option or hdd-utility from manufacturer, etc.
vH
19:17
vnvisa.top Hello
Now I think of 2 options for me:

1/ use a HDD, it's ok although the speed of MDBX is a little slower than Iowow which I am using.

2/ use SSD and configure MDBX with option MDBX_WRITEMAP | MDBX_UTTERLY_NOSYNC then manually call mdbx_sync(env). It's extremly fast with HDD or SSD.
Л(
19:19
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Nevertheless, it is rational, reasonable enough and convenient for most laptop users (especially with a non-removable battery or with protection against unexpected disconnection).
vH
19:21
vnvisa.top Hello
Then I will by a new 3.5" desktop HDD to benchmark :)
VS
19:23
Victor Smirnov
In reply to this message
HDDs performance start degrading once your workload doesn't fit into the cache. And cache doesn't help with scattered reads. Using SSD is better if you can tolerate lazy sync, like, by relying on the battery in your laptop as an emergency power source.
👍
vH
7 January 2024
vH
08:29
vnvisa.top Hello
In the function mdbx_dbi_open, parameter name, can I mix the use of named db and NULL db?
I want to use NULL to store some internal private data.
8 January 2024
vH
14:15
vnvisa.top Hello
The answer is NO, I have to use another named db
10 January 2024
AS
12:31
Alex Sharov
Is it possible to read pageSize before env_open ?
Л(
12:34
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Технически это возможно, так как формат БД зафиксирован и размер страницы не меняется после создания БД.
Поэтому можно просто прочитать заголовок нулевой мета-страницы.
Однако, в текущем API такое не реализовано.
Сделайте FR если нужно, тогда попробую набросать по-быстрому.
👍
LP
AS
12:40
Alex Sharov
🤝
Л(
11 January 2024
vH
07:51
vnvisa.top Hello
How to fix this error?

mdbx_cursor_close: Assertion `mc->mc_signature == MDBX_MC_LIVE || mc->mc_signature == MDBX_MC_READY4CLOSE' failed
vH
08:16
vnvisa.top Hello
I want to iterate through all key/val pairs using cursor_open
Then call cursor_get with flag MDBX_FIRST (if count == 0) or MDBX_NEXT (if count > 0)
Л(
10:20
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Please read the API description. At least one time the whole mdbx.h.

There is no other way.
vH
11:01
vnvisa.top Hello
I read this
For example, to list all key-value pairs in a database, use operation MDBX_FIRST for the first call to mdbx_cursor_get(), and MDBX_NEXT on subsequent calls, until the end is hit.
11:08
mdbx_cursor_get()
LIBMDBX_API int mdbx_cursor_get ( MDBX_cursor * cursor,
MDBX_val * key,
MDBX_val * data,
MDBX_cursor_op op
)

Retrieve by cursor.

This function retrieves key/data pairs from the database. The address and length of the key are returned in the object to which key refers (except for the case of the MDBX_SET option, in which the key object is unchanged), and the address and length of the data are returned in the object to which data refers.
11:09
It gets the first key/val successfully, but not the 2nd forward
Л(
11:16
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Ok, but what about txn boundary and cursor lifetime?
vH
11:27
vnvisa.top Hello
my code flow like this (pseudo code)
env_open
txn_begin
dbi_open
cursor_open
count = 0

loop do
rc = cursor_get(cur, key, val, count == 0 ? first : next)
break if rc != 0
do_something_with_kv
count++
end_loop

txn_renew
cursor_close
11:34
Oops, my code fault. I have to put the
txn_renew

outside of the loop block
11:35
Thank you
🤝
Л(
16 January 2024
Jahr invited Jahr
18 January 2024
vH
12:27
vnvisa.top Hello
inside a write transaction, does the data available immediately before commit (for other read txn to query data)?
СМ
12:29
Сергей Мирянов
no, libdmbx uses snapshot isolation
vH
12:29
vnvisa.top Hello
Great, thank you.
vH
13:47
vnvisa.top Hello
Although I open_env with flag MDBX_WRITEMAP | MDBX_UTTERLY_NOSYNC and txn_begin with flag MDBX_SAFE_NOSYNC | MDBX_NOMETASYNC to create each write then commit transaction, it's very slow.

Compare to a huge many writes within only one transaction (one commit at the end), it's much extremely faster.

Does it because of snapshot isolation overhead?
30 January 2024
Andrew invited Andrew
3 February 2024
Slava invited Slava
8 February 2024
mm invited mm
11 February 2024
hoang nguyen invited hoang nguyen
Simon invited Simon
15 February 2024
Л(
15:52
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Сейчас нет, но не совсем понятно что именно вам нужно.

Заведите FR на gitflic, опишите что именно требуется. Лучше прям сейчас. На этой неделе, думаю, у меня будет время, т.е. постараюсь сделать.
VS
16:00
Victor Smirnov
Такое может быть нужно, если хочется читать БД в несколько потоков, и чтобы каждый поток видел одинаковый снимок БД.
Л(
16:26
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Сейчас я другим занят, но дежавю что это уже сделано
VS
17:10
Victor Smirnov
Это обычно нужно для аналитики (многопоточное выполнение запросов). Но, поскольку в MDBX нельзя разделить БД на диапазоны с равным количеством записей в них, оно не сильно интересно (для аналитики).
A
17:57
Alexander
А в чем трудность, если писатель всегда один? приостановить писателя, запустить сколько угодно читателей (по факту все видят один snapshot).
Аналогичный эффект можно и через LVM получить (если нет контроля над писателем)
"Диапазоны с равным количеством записей" - mdbx_estimate_range() + метод дихотомии, можно произвольные данные разбить на примерно равные диапазоны (для задач MPP), разве нет?
VS
18:02
Victor Smirnov
In reply to this message
Писатель не "один", их много, но они сериализованы, и сами борются за блокировку. "Остановить" их будет проблематично. Гораздо проще сделать расширение API, которое будет возвращать массив транзакций с одинаковым txid. Просто надо обосновать, зачем оно надо, так как этот API может быть редко используем.
A
18:04
Alexander
в момент времени писатель всегда один. в чем суть проблематичности сделать глобальный мутекс на commit на уровне приложения?
VS
18:10
Victor Smirnov
In reply to this message
Это "костыль". Тут надо расширять btree и хранить количество ключей в поддереве. Для MPP MDBX не годится в любом случае из-за своего механизма управления памятью, который не любит долгие транзакции. А MPP — это именно долгие запросы: часы и дни. Для потоковой аналитики — да, можно использовать, но нужно накручивать сверху репликацию. MDBX — это маленькая простенькая встраиваемая СУБД, которая, благодаря CoW, умеет гораздо больше, чем обычно надо в этой нише. Но у неё отсутствуют "энтерпрайзные" фичи, которые могли бы позволить ей из этой ниши выйти.
18:11
In reply to this message
Он всегда один именно из-за этой блокировки на commit, которая там уже есть.
A
18:13
Alexander
Да, она там уже есть, но читатели не могут (или могут?) получить к ней доступ, чтоб получить взаимно согласованный снепшот.
Что опять-же не мешает вкрутить любой согласующий механизм сверх библиотечного.
18:18
Костыль не костыль, но цель data range estimation вполне достижима. А что лучше - MDBX или какая Netezza - это out-of-scope.
Аналитка данных общего вида априори подразумевает columnar compression (см какой Clickhouse или KDB+), тут MDBX сказать ничего не может, и не должен был, он изначально вроде как про первичную OLTP обработку данных
VS
18:18
Victor Smirnov
In reply to this message
Не надо никаких специальных механизмов. Нужно просто, чтобы txn_start() (или как он там называется) возвращал массив объектов заданной длины, вместо одного. И всё. Просто эта фича потащит за собой сценарии использования, для которых MDBX не предназначена по своему дизайну.
18:18
In reply to this message
Вот именно)
18:19
Короткие точечные транзакции.
18:32
В общем случае, estimate_range нужен для планировщика запросов, а не для распараллеливания. Распараллеливание имеет смысл, когда у нас число конкурентных запросов значительно меньше числа процессоров (и каналов в storage) (десятки запросов и тысячи процессоров). Для OLTP же у нас обычно всё наоборот (тысячи запросов и десятки ядер).
👍
A
A
18:59
Alexander
Распараллеливание и есть частный случай планировщика запроса (один из вариантов посчитать агрегаты).
А так есть в LMDB еще и концепция nested (parent) transactions, но она похоже (могу ошибаться) только про писателей (аналог SAVEPOINTs?). В теории можно было бы и на читателей распространить - стартуем головного читателя, в нем делаем дочерние транзакции, все они согласованы.
VS
19:02
Victor Smirnov
Планировщик (оптимизатор) запросов для OLTP-применений не делает распараллеливание)
19:04
In reply to this message
Это теоретически можно, да, базовые алгоритмы это позволяют. Но придется блокировками защищать внутренние структуры данных. Может быть полезно тогда, когда писатель делает много "тяжелой" работы, например, ETL.
A
19:47
Alexander
Не уверен насчет блокировок, по идее там все предельно просто - все эти вложенные транзакции чтения должны просто гарантированно смотреть на один и тот-же корневой узел.
Да, c nested/parent тут переусложнение (они изначально в LMDB и в MDBX for write transactions only), в идеале это все должно быть нечто вроде:

MDB_txn * mdb_txn_clone(uint64_t txnid);

Но опять-же, в виду возможности повесить общий согласующий mutex на писателей и читателей (если не хочется в sudo lvcreate) - это все скорее nice to have
VS
20:14
Victor Smirnov
In reply to this message
Зависит от реализации. Я уже не помню, что там в кишках у MDBX, но могут быть не только функциональные структуры данных, а еще всякие разделяемые кэши, GC, и т.п. Оно всё не сильно проблематичное. Я сам для своих CoW SWMR дизайнов вариант распараллеливания внутри писателя рассматриваю как рабочий.
18 February 2024
DH
10:28
Dvir H
I think there is a bug with DUPFIXED. The following code fails:
#include "internals.h"

int main() {
int rc;
MDBX_env *env = NULL;
MDBX_dbi dbi = 0;
MDBX_val key, data;
MDBX_txn *txn = NULL;

rc = mdbx_env_create(&env);
if (rc != MDBX_SUCCESS) {
fprintf(stderr, "mdbx_env_create: (%d) %s\n", rc, mdbx_strerror(rc));
exit(EXIT_FAILURE);
}

rc = mdbx_env_set_maxdbs(env, 1);
if (rc != MDBX_SUCCESS) {
fprintf(stderr, "mdbx_env_create: (%d) %s\n", rc, mdbx_strerror(rc));
exit(EXIT_FAILURE);
}

rc = mdbx_env_open(env, "./example-db",
MDBX_NOSUBDIR | MDBX_COALESCE | MDBX_LIFORECLAIM, 0664);
if (rc != MDBX_SUCCESS) {
fprintf(stderr, "mdbx_env_open: (%d) %s\n", rc, mdbx_strerror(rc));
exit(EXIT_FAILURE);
}

rc = mdbx_txn_begin(env, NULL, 0, &txn);
if (rc != MDBX_SUCCESS) {
fprintf(stderr, "mdbx_txn_begin: (%d) %s\n", rc, mdbx_strerror(rc));
exit(EXIT_FAILURE);
}

rc = mdbx_dbi_open(txn, "test", MDBX_DUPSORT | MDBX_DUPFIXED | MDBX_CREATE, &dbi);
if (rc != MDBX_SUCCESS) {
fprintf(stderr, "mdbx_dbi_open: (%d) %s\n", rc, mdbx_strerror(rc));
exit(EXIT_FAILURE);
}

char key_bytes[32] = {0};
key.iov_len=32;
key.iov_base=key_bytes;

// Another put after this will fail.
unsigned char idx;
for (idx=0; idx<129; idx++){
char data_bytes[15]={idx};
data.iov_len=15;
data.iov_base=data_bytes;
rc = mdbx_put(txn, dbi, &key, &data, 0);
if (rc != MDBX_SUCCESS) {
fprintf(stderr, "mdbx_put: (%d) %s\n", rc, mdbx_strerror(rc));
exit(EXIT_FAILURE);
}
}

// This will fail and exit.
char data_bytes[15]={idx};
data.iov_len=15;
data.iov_base=data_bytes;
rc = mdbx_put(txn, dbi, &key, &data, 0);
if (rc != MDBX_SUCCESS) {
fprintf(stderr, "mdbx_put: (%d) %s\n", rc, mdbx_strerror(rc));
fprintf(stderr, "expected failure\n");
exit(EXIT_FAILURE);
}

rc = mdbx_txn_commit(txn);
if (rc) {
fprintf(stderr, "mdbx_txn_commit: (%d) %s\n", rc, mdbx_strerror(rc));
exit(EXIT_FAILURE);
}
}
🤔
Л(
Л(
15:40
Леонид Юрьев (Leonid Yuriev)
@Dvirsw, thank fior reporting.
I will dig shortly.
👍
DH
Л(
19:05
Леонид Юрьев (Leonid Yuriev)
@Dvirsw, seems this is like "not a bug but feature" ;)

Actually no DB corruption, but false-positive error because the extra/unneeded checks.
An odd length of dupfixed items and an odd number of ones on a leaf2-page triggers the issue.

Nonetheless I will check/dig more.
19 February 2024
Артур invited Артур
20 February 2024
DH
10:57
Dvir H
In reply to this message
There is any update about that?
Л(
11:27
Леонид Юрьев (Leonid Yuriev)
The fix now is available in the devel branch.
Your testcase was added also.
But I haven't finished refining the stochastic test yet.
👍
DH
DH
11:43
Dvir H
Thanks
21 February 2024
Л(
19:00
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Замеченная вами проблема, и в особенности тесткейс, подвигли меня на прояснение причины, по которой проблема не была выявлена стохастическим тестом.
Я нашел пару упущений/недочетов в моих тестах, но при их устранении обнаружилась более существенная проблема.

В текущем понимании:
- суть: использование DUPFIXED (включая INTEGERDUP) может приводить к повреждению БД и/или потере данных.
- вероятность проявления сильно увеличивается с увеличением размера/длины мульти-значений/дубликатов (не ключей).
- в MDBX проблема унаследована от LMDB, где существует более 11 лет, начиная с коммита https://github.com/LMDB/lmdb/commit/ccc4d23e749edc5ea461261427a0ee0a663fdfe5#diff-42e9f366b36288d7eff63efed75ac77cd11cb22e1693e7c9c60a8b3eb7e38b8cR4324 до настоящего времени.

Мне потребуется некоторое время чтобы тщательно (пере)проверить исправления и прогнать тесты.
👍
DH
AS
VS
DH
20:12
Dvir H
In reply to this message
Thank you for the warning. Just to make sure, there are no known issues or concerns with the DUPSORT flag?
Л(
20:29
Леонид Юрьев (Leonid Yuriev)
In reply to this message
The trouble is with an internal mechanics of LEAF2-pages.
Such pages are a special kind of leaf pages for fixed/same-length dupsort values.

Thus the issues is with MDBX_DUPFIXED and MDBX_INTEGERDUP, but no with MDBX_DUPSORT.
Hovewer, MDBX_DUPFIXED and MDBX_INTEGERDUP implies MDBX_DUPSORT.
So, exactly:
- MDBX_DUPSORT | MDBX_DUPFIXED — affected;
- MDBX_DUPSORT | MDBX_INTEGERDUP — affected;
- MDBX_DUPSORT — not affected.
👍
DH
23 February 2024
Ant Kyt invited Ant Kyt
26 February 2024
AK
11:10
Ant Kyt
Уважаемые коллеги, приветствую.

Уверен, что для вас этот вопрос тривиальный,
но подскажите пожалуйста, как собрать свежую версию mdbx
для Windows в режиме Release ?

1) Среда разработки Windows 10 (64bit)
2) Девелоперская машина с Visual Studio 2022
3) Установлен CMake 3.28.3

4) Исходники mdbx взяты с GitFlic и склонированы в папку
c:\gitflic\libmdbx\

Далее в корне дерева создана папка release и из девелоперской
консоли сделан переход в неё.
то есть сейчас я нахожусь в пустой папке
c:\gitflic\libmdbx\release\

5) Команда
cmake -DCMAKE_BUILD_TYPE:STRING=Release ..

успешно генерит множество файлов проектов.


6) Затем вызов
cmake --build .

Собирает исполняемые файлы в директорию
c:\gitflic\libmdbx\release\Debug\

И на консоль выводится запись
Configuration-depended MDBX_BUILD_TYPE: Debug

При этом размер полученного файла mdbx.dll около 987 килобайт.

Хотелось бы по возможности получить релизную сборку.
Заранее благодарен.


П.С. Мой основной стек — C# , поэтому такой детский вопрос про CMake .
Хочу стряхнуть пыль с проекта MDBX.NET ,
в котором последний комит был 6 лет назад и в котором версия mdbx.dll
тоже шестилетней давности.

https://github.com/wangjia184/mdbx.NET
i
11:11
igors
Я уже встряхнул)))
11:11
Правда пока в паблик не постил
11:12
ну и я по факту сделал похожий API как на с++
Л(
11:14
ну а по факту, я бы не советовал вот это вот - https://github.com/wangjia184/mdbx.NET, так как у него не эффективно сделан маршалинг, что существенно сказывается на производительности
AK
11:27
Ant Kyt
In reply to this message
Рад бы "не использовать",
но пока не нашел других обвязок mdbx <—> C#
Может, подскажете что-то?


П.С. Очевидно, некоторое время назад начал возиться со
"старой" LMDB (в связке с Lightning.NET ),
но первые тестовые приложения ведут себя нестабильно.

То при первом создании базы странные ошибки кидают,
то ещё что-то.
Или могут проработать несколько суток, а потом сойти с ума
и перестать делать запись в базу.
i
11:29
igors
In reply to this message
попробуй Lightning.NET на маке или линуксе, сравни перф
11:29
In reply to this message
ничего нет, пока
11:30
я планировал в ближайшее время в паблик выложить
11:31
может на след недели
AK
11:34
Ant Kyt
In reply to this message
Поскольку он себя ведет нестабильно, вопрос производительности
отходит на второй план. И тут уже не важно какая ОС.

Видимо, это те застарелые болячки, которые когда-то заставили
начать разработку отдельной MDBX ...
11:37
In reply to this message
Звучит прекрасно. 👍
Пока продолжу ковырять старую обертку.
Может, пойму что там за проблема с маршалингом.
VS
11:49
Vladislav Shchapov
In reply to this message
В Windows cmake по умолчанию использует генератор Visualstudio, который в рамках одного проекта поддерживает несколько типов сборок (дебаг,релиз,..)

В связи с этим на этапе конфигурирования указывать «-DCMAKE_BUILD_TYPE:STRING=Release» безсмысленно.

На этапе сборки надо добаить указание на тип сборки. Например:

cmake --build . --verbose --config Release
AK
11:55
Ant Kyt
In reply to this message
Получилось.
Большое спасибо!
🙏
Л(
15:57
Леонид Юрьев (Leonid Yuriev)
В продолжение темы устранения замеченной проблемы и её устранения.

Собственно суть в том, что в упомянутом коммите от 2012 года в LMDB была добавлена логика резервирования дополнительного места при создании или расширении под-страницы типа P_LEAF2, а ошибка в том что из-за резервирования размер может получиться больше размера страницы БД (происходит переполнение, а далее повреждение структуры БД, стека и/или роспись памяти, etc).
Это один из последних "ребусов" в коде Говарда Чу, который я до последнего порешил из принципа "работает — не трогай".

Черновое исправление проходит тестирование и пока не доступно публично.
Попутно также поправлен еще один (очень замысловатый) недочет в логике отслеживания и корректировке позиций курсоров стоящих на соседних позициях, который проявлялся при разделении страницы со вставкой пустой станицы слева (LMDB так не умеет).

Однако, первопричина проблемы в отсутствии внятной и продуманной тактики/стратегии резервирования места в sub-страницах. Фактически в LMDB в 12-ом году был добавлен некий костыль, который (видимо) улучшал производительность в каких-то сценариях и одновременно содержал критическую ошибку приводившую к повреждению БД.
Соответственно, полноценное исправление предполагает выработку такой стратегии/тактики/логики.

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

Сильно расписывать решение я не хочу и думаю будет достаточно copy&paste фрагмента кода с комментариями:
            growth = IS_LEAF2(fp)
? EVEN(fp->mp_leaf2_ksize)
: (node_size(data, nullptr) + sizeof(indx_t));
if (page_room(fp) >= growth) {
/* На текущей под-странице есть место для добавления элемента.
* Оптимальнее продолжить использовать эту страницу, ибо
* добавление вложенного дерева увеличит WAF на одну страницу.
*/
goto cow_subpage;
}
/* На текущей под-странице нет места для еще одного элемента.
* Можно либо увеличить эту под-страницу, либо вынести куст
* значений во вложенное дерево.
*
* Продолжать использовать текущую под-страницу возможно
* только пока и если размер после добавления элемента будет
* меньше me_leaf_nodemax. Соответственно, при превышении
* просто сразу переходим на вложенное дерево. */
xdata.iov_len = olddata.iov_len + growth;
if (xdata.iov_len > env->me_leaf_nodemax)
goto prep_subDB;
15:57


/* Можно либо увеличить под-страницу, в том числе с некоторым
* запасом, либо перейти на вложенное поддерево.
*
* Резервирование места на под-странице представляется сомнительным:
* - Резервирование увеличит рыхлость страниц, в том числе
* вероятность разделения основной/гнездовой страницы;
* - Сложно предсказать полезный размер резервирования,
* особенно для не-MDBX_DUPFIXED;
* - Наличие резерва позволяет съекономить только на перемещении
* части элементов основной/гнездовой страницы при последующих
* добавлениях в нее элементов. Причем после первого изменения
* размера под-страницы, её тело будет примыкать
* к неиспользуемому месту на основной/гнездовой страницы,
* поэтому последующие последовательные добавления потребуют
* только передвижения в mp_ptrs[].
*
* Соответственно, более важным/определяющим представляется
* своевременный переход к вложеному дереву, но тут достаточно
* сложный конфликт интересов:
* - При склонности к переходу к вложенным деревьям, суммарно
* в БД будет большее кол-во более рыхлых страниц. Это увеличит
* WAF, а также RAF при последовательных чтениях большой БД.
* Однако, при коротких ключах и большом кол-ве
* дубликатов/мультизначений, плотность ключей в листовых
* страницах основного дерева будет выше. Соответственно, будет
* пропорционально меньше branch-страниц. Поэтому будет выше
* вероятность оседания/не-вымывания страниц основного дерева из
* LRU-кэша, а также попадания в write-back кэш при записи.
* - Наоботот, при склонности к использованию под-страниц, будут
* наблюдаться обратные эффекты. Плюс некоторые накладные расходы
* на лишнее копирование данных под-страниц в сценариях
* нескольких обонвлений дубликатов одного куста в одной
* транзакции.
*
* Суммарно наиболее рациональным представляется такая тактика:
* - Вводим три порога subpage_limit, subpage_room
* и subpage_reserve, которые могут быть заданы/скорректированы
* пользователем в ‰ от me_leaf_nodemax;
* - Используем под-страницу пока её размер меньше subpage_limit
* и на основной/гнездовой странице не-менее subpage_room
* свободного места;
* - Резервируем место только для 1-3 коротких dupfixed-элементов,
* расширяя размер под-страницы на размер кэш-линии ЦПУ, но
* только если на странице не менее subpage_reserve свободного
* места.
* - По-умолчанию устанавливаем subpage_limit = me_leaf_nodemax (1000‰),
* subpage_room = 0 и subpage_reserve = me_leaf_nodemax (1000‰).
*/
27 February 2024
Роман invited Роман
AK
14:02
Ant Kyt
Разрешите пару вопросов:

в1)
Насколько знаю, memory mapped file может полностью располагаться в памяти
и жить без связи с файлом на жестком диске.

Можно ли сконфигурировать MDBX работать в режиме "только память"?

У этого режима предполагаю 2 достоинства:
1) Скорость вставок/чтений должна быть ещё выше
2) При интенсивном потоке вставок/апдейтов меньше насилия над SSD (меньше износ)
3) После креша или штатного закрытия "писателя" все записи сразу умрут, а
все читатели сразу поймут, что данные в этом кеше больше недоступны.


в2) Может ли MDBX работать в режиме "несколько отдельных процессов ПИШУТ в одну
базу
"?

LMDB допускает такой режим.
Л(
14:08
Леонид Юрьев (Leonid Yuriev)
In reply to this message
в1) Вам достаточно разместить файл в /dev/shm и вызывать mdbx_env_copy() (либо утилиту mdbx_copy) для копирования данных "из памяти" когда посчитаете необходимым.

в2) Да.
В целом, MDBX умеет и может больше/быстрее/надежнее чем LMDB, за вычетом проблемных режимов (без блокировок и т.п., с которыми портачат 99% пытающихся их использовать разработчиков).
👍
AK
A
14:38
Alexander
Полностью в памяти - это требование, ограничить расположение данных именно на физическом уровне в микросхемах ОЗУ?
Или просто пожелание минимизировать дисковые операции, тогда чем MDBX_UTTERLY_NOSYNC | MDBX_WRITEMAP не устроит?

А есть еще tmpfs (shmfs),

и ramfs, она прям вообще без backing store (т.е. декларируется, что только на микросхемах RAM)
AK
15:36
Ant Kyt
In reply to this message
п1) > "чем MDBX_UTTERLY_NOSYNC | MDBX_WRITEMAP не устроит?"

1.1) Флаги открытия базы / environment ни разу не очевидны для нового пользователя.
В данный момент мне сильно не хватает понимания ответить Вам на этот вопрос,
потому что не могу сообразить к каким эффектам это приведет на практике. ☺️

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

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


п1.2) С точки зрения достоверности данных ещё раз подчеркну,
что режим работы "в момент поломки писателя вся база закрывается и
становится принципиально недоступной для читателей
" является желательным.

И достигается как раз режимом "MMF без привязки к физическому файлу"
(насколько мне известно).



п2) > "А есть еще tmpfs (shmfs), и ramfs,"

2.1) Понятно, что "просто разметить что-то в памяти не означает,
что эти данные НЕ будут свопиться на диск
".

Общая идея жить без файла —
максимизировать показатели "write/read transaction per sec".


2.2) Что касается "ramfs, ..." с точки зрения пользователя хотелось бы,
чтобы сама библиотека выбирала конкретное решение для конкретной ОС
и скрывала детали реализации.
А мы только декларировали своё желание
"разместиться в памяти без физического файла".
A
15:44
Alexander
2.2) для ramfs вроде как даже гарантируется отстуствие связи с диском https://docs.kernel.org/filesystems/ramfs-rootfs-initramfs.html#what-is-ramfs
хотя если это все будет крутиться в каком VMWare, то возможносвоппинг может уже быть на уровне гипервизора
15:48
1.1) 1.2) тут какие-то противоречия. если данные в только в оперативной памяти (т.е. живут исключительно между ребутами/рестартами приложения), то какой смысл тогда вообще заморачиваться с LMDB/MDBX? Можно просто сделать межпроцессный mmap(), и реализовать в ней btree и rwlock, в интернетах разбросаны тысячи готовых примеров, offset pointer и т.д.
AK
15:54
Ant Kyt
In reply to this message
"Готовые примеры" на то и "примеры",
что могут упускать из виду что-то критически важное
или содержать неочевидные ошибки.

Если бы всё было так просто, уважаемый Леонид и другие не менее
уважаемые авторы mdbx не заморачивались бы с проектом
на 2 мегабайта сишных исходников. 😌
Л(
16:37
Леонид Юрьев (Leonid Yuriev)
In reply to this message
На все ваши вопросы можно дать один ответ - RTFM и гуглите.
Это может показаться обидным, но вся информация действительно есть в документации и в Сети (в том числе материалы/статьи как по внутреннему устройству, так и по использованию LMDB, включая мой доклад на Highload в 2015).

В libmdbx не является образовательным/обучающим проектом и предназначен для разработчиков (а не пользователей).
Поэтому в документации действительно много умолчаний, опирающихся на принцип "это очевидно" исходя из базовых знаний необходимых разработчику в области хранения данных (именно разработчику, а не пользователю условного oracle, ms-sql и/или ms-access).
Например, в документации не расшифровываются последствия аварии до сброса всех данных на диск в "хрупких" режимах работа, так как специалист должен знать принципы устройства b-tree и организации отложенной записи, а следовательно представлять умалчиваемые подробности.

По вашим вопросам видно что вы пока не ознакомились с документацией.
Могу только посоветовать внимательно прочитать её два раза (в том числе mdbx.h), а также поискать статьи по LMDB/MDBX в Сети.
Джедайский путь = разобраться и опубликовать хорощую статью, либо хотя-бы оформить пару коммитов в документацию.
💯
?
VS
16:57
Victor Smirnov
Поддержу участников, порекомендовавших размещать БД в tmpfs, примонтированной к RAM. Внимание, swapping в этом режиме возможен, это уже как менеджер памяти решит, но маловероятен. Отключить запись на диск для mmap-файла в Linux стандартными способами нельзя. Т.е. размещать БД в "ramfs" есть смысл, и так делают. Делать для этого отдельный режим в БД нет никакого смысла. Swapping возможен всегда. Те, кто хочет от него избавиться совсем, отключают swap и наслаждаются случайными срабатываниями oom-killer.
Р
17:06
Роман
День добрый. Вижу, что не первый интересуюсь - существует ли на сегодняшний день актуальная привязка к Python? Или этого уже не планируется?
i
17:09
igors
а вообще можно чатку поскролить вверх - там тоже много документации)))
A
17:16
Alexander
LMDB/MDBX это про ACID, в случае in-memory наверное можно сразу выкинуть D
А потом подумать и над остальными тремя буквами с поправкой на требования приложения и требования еще и архитектуры LMDB/MDBX (нетиповые, мягко говоря).

Реализовать же самому in-memory only btree структуру в MAP_SHARED mmap() области это задачка на один вечер (увы, готовых продуктовых решений гугл сходу не выдает).
Нам подобное понадобилось чтоб побороть ограничения LMDB на "одного писателя в момент времени". Плюс реализовать эдакий transactional memory с откатами в случае crashes.
В приложении специально был выделен только один процесс, Database Writer, которому все остальные пользовательские процессы
(которые с потенциальными danging-pointer и т.п. крешами) передавали в конце своих транзакций дельты изменений в виде этих b-tree деревьев в mmap(), в своих отдельных областях для каждого такого пользовательского процеса.
А этот Database Writer занимается только тем, что делает merge этих дельт, полученных от других процессов и уже основной базы данных на диске. Предварительно верифицировав дельту.
Пользовательские процессы аналогично - для своих операций чтения смотрели сначала в свою "грязную" локальную дельта базу за пока незакомиччеными изменениями, а потом уже ходили в LMDB в режиме read-only.

Но это мы просто делали параноидальную защиту от danging ponter в "пользовательском коде" + приближали поведение к привычным "длинным" конкуретным транзакциям на запись как в обычных SQL базах, делая предположение на то, что код выделенного процесса Database Writer стабилизирован и не может разрушить базу данных и вообще цивилизацию.

Но в LMDB/MDBX ничего подобного (локальный накопительный буфер незакомиченных изменений для "длинных" незакомиченных транзакций), увы, нет, как минимум пока.

Кстати неплохо было бы и получить (равно как и time-series партицирование, которое тоже каждому проекту нужно пилить каждый раз самому).
17:23
time-series партицирование - это про жизненный цикл данных, когда некий timestamp определяет время жизни записи, т.е. время удаления устаревших кусков базы данных (ротация логов как типовой пример), плюс гранулированные по диапазонам этих timeseries бекапы и т.п.
сейчас нужно делать создание или кучи databases + несколько курсоров со слияниями, или включать timestamp в состав ключа и удалять потом каждую запись отдельно, что еще хуже

не бог весть что, но каждому такое пилить самому тоже так себе задача. в SQL же базах подобное давно есть из коробки.
Л(
17:30
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Там же пусто, или я чего-то не вижу?
i
17:53
igors
In reply to this message
честно даже не проверял
17:57
https://github.com/igor-sidorovich/Libmdbx.Net - C# .Net API for libmdbx, API is really similar with the C++ API.
Л(
18:00
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Сам я заниматься этим не планирую, а о появлении новых привязок мне не сообщали.

Там вроде-бы ничего сложного, если не пытаться сделать больше чем просто протащить "сишное" API.
Однако, насколько я помню, возникают какие-то жуткие/странные проблемы со сборкой libmdbx "изнутри" pip и т.п.

Если же не делать кальку с "сишного" API или каких-то привязок, а стараться сделать что-то похожее на C++ API, то все сильно сложнее — нужно не только хорошо жнать внутреннию механику pip и различных вариантов питона, но и иметь хорошее видение стиля/архитектуры API.
👍
Р
28 February 2024
Л(
13:35
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Да.
👍
AS
13:47
In reply to this message
На всякий уточню, что причина не в локах/блокировках, а в неотъемлемых свойствах "сетевых дисков" — запись выполненная одним процессом не становиться автоматически видна в memory-mapped другим процессам.
👍
AS
Л(
15:26
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Да, возможно, если используете именно mdbx_txn_abort().
Память под readonly-транзакции выделяется через malloc() и освобождается через free().

В подобных сценариях лучше использовать mdbx_txn_reset() (транзакция будет прервана, но экземпляр не освобожден) и затем mdbx_txn_renew().
Л(
21:02
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Да, это баг, а не фича.
Посмотрю-поправлю.
Спасибо что сообщили.
👍
VS
AS
1 March 2024
Maria invited Maria
2 March 2024
Л(
01:21
Леонид Юрьев (Leonid Yuriev)
In reply to this message
$ git grep "to enumerate the reader lock table"
mdbx.h:/** \brief A callback function used to enumerate the reader lock table.
👍
AS
4 March 2024
Л(
15:02
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Поправил.
https://gitflic.ru/project/erthink/libmdbx/commit/dbe2b2928472aca10b05e000f3f6963b7bbf613d
В текущем понимании, никаких проблем/последствий кроме срабатывания assert-а нет.

Также добавлен простенький тест.
https://gitflic.ru/project/erthink/libmdbx/commit/c6df99968867e0ca8bc259e78224d7ca6006b060

На этой неделе планирую перенести эти правки в master и затем в stabe.
🔥
AS
15:10
In reply to this message
В ветке devel подготовлено несколько существенных доработок, включая полное устранение проблемы.
Тесты еще в процессе, но уже понятно что можно пробовать использовать эту версию для разработок.

Надеюсь закончить тестирование на этой неделе, портировать часть доработок в master, затем выпустить релиз с переносом в ветку stable, а потом влить devel в мастер и сделать релиз новой ветки.
Суть = текущая ветка devel скоро пойдет в mainstream и поэтому есть смысл её пробовать/тестировать.
👍
DH
15:12
In reply to this message
Пришлось несколько раз откладывать.
Но сегодня хочу сделать.
Л(
20:42
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Все эти флаги/режимы исторически унаследованы от LMDB, а я их бережно не выбрасываю, но и не-переделываю (не ломаю совместимость).
Но при этом мне частенько сложно понять обоснования, по которым Говард Чу сделал именно так.

Так вот, MDBX_INTEGER_KEY это только ограничения на размер ключа и фиксированный встроенный компаратор (функция сравнения).
Структура страниц при этом не меняется.

+ Соответственно,основной выигрыш - отсутствие вызова memcmp() при сравнении ключей.
👍
AS
20:57
Ну да, всё видно в исходном коде )
🤝
AS
5 March 2024
6 March 2024
Волька invited Волька
Л(
22:45
Леонид Юрьев (Leonid Yuriev)
В ветке master подготовлен версия-кандидат для выпуска v0.12.10.
Устранена одна серьезная проблема и несколько второстепенных.
Подробности в ChangeLog.
👍
A
N
DH
8
8 March 2024
Л(
19:31
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Видимо баг просочился, посмотрю.
11 March 2024
Eugene Zaitsev invited Eugene Zaitsev
12 March 2024
nicm invited nicm
nicm invited nicm
n
13:05
nicm
Really cool to see this project is still alive 👍 I really thought it got abandoned in 2022.
n
13:29
nicm
Why did this project split off from LMDB in 2015?

I wonder, why LMDB didn't stay joined with you, to make together a superior product?
UD
13:30
Uncel Duk
NIH syndrome mostly, you could check details about reopenldap project
👌
n
n
15:08
nicm
Does MDBX support using (in particular aborting/closing) the same write transaction on different threads? LMDB allows this with MDB_NOLOCK.
Л(
17:35
Леонид Юрьев (Leonid Yuriev)
In reply to this message
No.

Support of MDB_NOLOCK was removed from MDBX since it actually was a bad feature:
- provokes developers to go the wrong way and make bugs;
- increased the number of claims and requests to deal with heisenbugs, coredump, DB corruption, etc;
- it complicated the logic, increased the number of conditional jumps and prevented the implementation of (1) more control for robustness and safety, (2) other features like dynamic DB size manageme;
- etc...
👌
n
17:42
In reply to this message
Seems github was restore some inconsistnt snapshot of my repos and you cold try machine translation.

Actual repo for ReOpenLDAP is https://gitflic.ru/project/erthink/reopenldap and the latest news is https://www.opennet.ru/opennews/art.shtml?num=57753
DH
18:38
Dvir H
Is each mdbx_put operation updating the pages from the changed leaf page to the root?
If so, do I have a way to tell the database I am putting several values that I know will be in the same area of the tree and make it do those updates only once?
In general, do I have a way to put a large number of values in a single transaction to speed up the put operation? The closest thing I see in the documentation is the MDBX_MULTIPLE, but it's a very specific option.
Л(
18:57
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Yes.
The leaf-page should be CoWed for shadow paging, then the its parent page should be CoWed to update reference to the new leaf, and so forth up to root.

You should do nothing other than just make most of such mdbx_put() with the one transaction.
19:10
In reply to this message
To speed up the insertion of a large number of elements:
- use a cursor API, i.e. mdbx_cursor_put();
- try using MDBX_APPEND and/or MDBX_APPENDDUP;
- put itens in sorted order, i.e. presort ones before insertion;
n
19:10
nicm
In reply to this message
I have (usually a rare edge case but a case nonetheless) where I want write txn aborting to happen on garbage collection (I use C libraries with foreign-function interfaces). The GC won't occur on the same thread.

Any ideas on how to deal with this case, or is this really impossible?
DH
19:29
Dvir H
In reply to this message
Thank you for the answer and advices.
Should I expect a speed-up in the write performance if I use mdbx_cursor_put and pre-sort the values in a single transaction? In my small experiments, I didn't see any effect of this approach.
Just to be clear, I am talking about the case where the keys in a transaction are sorted but not continuous in the database (for example, I have the keys 1,2,3...1000 in the database, and I upsert the keys 4,100,300,500 in a single transaction).
Does also when using the APPEND flag, the same branch pages to the root be updated for any append operation?
Л(
19:44
Леонид Юрьев (Leonid Yuriev)
In reply to this message
1. Whole internals of txn is strictly single-threaded. So anyway you must not do anything with the same txn from different threads simultaneously.

2. To abort write txn from different/foreign thread (a thread which don't started the txn) you need just two points:
- Used locking scheme (see MDBX_LOCKING build option) must be able to release the lock from a different thread (which not owned the lock). This is system depended;
- Checking for txn owner should be disabled (see MDBX_TXN_CHECKOWNER build option).
👍
n
Л(
20:09
Леонид Юрьев (Leonid Yuriev)
In reply to this message
1.
APPEND-options are for monotonic append-like insertions. So not for your case.

2.
90-99% of CPU time is spent on copying and modifying database pages in RAM.
90-99% of wall-clock time is spent on writing and flushing updated DB pages to the SSD/HDD/etc.
So, it is unlikely that you will get a significant speedup, but you can try to reduce overhead costs (including more cache-frendelity, better branch prediction, etc).
👍
DH
n
20:19
nicm
In reply to this message
Nice insights! Thank you.

Do you known which MDBX_LOCKING is/are selected these days on modern Linux,Windows,Mac systems on x86_64 and aarch64 (latest macs)? (And recent mobile phones/tablets of course, forgot about those)
Л(
20:39
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Always MDBX_LOCKING_WIN32FILES on Windows, no options.

The MDBX_LOCKING_SYSV (SystemV IPC semaphores) on all Macs, as far as I remember (since Apple don't able to implement shared posix mutexes nor futexes).

MDBX_LOCKING_POSIX2008 (Robust Mutexes) or MDBX_LOCKING_POSIX2001 (Shared Mutexes) on Linux, depending on version and build-time enabled features of glibc/musl and kernel.
👍
n
n
20:45
nicm
In reply to this message
Which of those support release (in this case from a mdbx write txn abort) from a different thread?
n
23:28
nicm
From my browsing, it seems win32 have no problems with mutex unlock on different threads: https://learn.microsoft.com/en-us/windows/win32/sync/mutex-objects

But Linux seems less certain and BSD and Darwin (Apple) seems not at all.

What do you think?
23:33
If I'm right, this problem is not solvable (for all modern, major systems) with MDBX and I have to stick with LMDB/MDB_NOLOCK.

However LMDB/MDB_NOLOCK has its own problems: If other processes try to access the same database file on disk at same/similar times, the file could get corrupt 😢

So the problem (aborting txn on GC) seems to have no really good solutions at the moment on LMDB or MDBX.

I hope I'm wrong.
13 March 2024
Л(
00:05
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Please do not ask me to verify the information you have found about the operating system API and the conclusions you have drawn.
You should either count on yourself or give up.
00:09
In reply to this message
My answer, more precisely the advice, referred to the issue of abortion a write-txn from another thread.
However, I don't understand exactly why you need this, and especially what all this has to do with GC (Garbage Collection, aka freeDB or freeList).
n
01:05
nicm
In reply to this message
I was referring to the GC in the programming language I use (through which I call lmdb (or mdbx) funcs via FFI) and edge cases where txn aborting should happen at GC on a different thread.

Anyway, thanks for the advices.
Л(
01:08
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Hmm, I don't understand the reasons why you need to abort a writing transaction when the runtime (from which libmdbx is called via FFI) starts the garbage collector.
n
01:18
nicm
In reply to this message
I'm using Haskell where so-called "asynchronous exceptions" can interrupt my program at any time. So the issue then becomes, dealing with any "dangling" transactions. (There are cases where "bracket" (specifying initialization and cleanup code up front) doesn't quite work and GC is more suitable for cleanup.)
Л(
01:22
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Got it.
It is ok/right to abort txn as a part of cleanup on exception handling, either asynchronous or synchronous.
01:30
@nicholasm28, just FYI.
Some time ago, I planned to implement an additional option of operation without binding to threads, similar to the MDBX_NOTLS option, but including write transactions.
This was planned to be implemented, in particular, for Rust bindings, which @vorot93 was engaged in (before the closure of the Akula project).
This feature could be rescheduled, if you become a tester.
AV
01:31
Artem Vorotnikov
In reply to this message
if you use Rust bindings, they do not require pinning to threads
01:32
write transactions are opened and closed using a separate 'standby' thread managed by Database (=environment) instance
01:34
when closing transaction, this thread accepts transaction pointer passed via channels
Л(
01:35
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Да, я помню что ты про это говорил.
Но у человека Хаскель и с точки зрения MDBX речь об отключении контроля соответствия thread_id и транзакции, через опцию режима работы с БД, а не посредством опции сборки библиотеки (как сейчас).
🤝
AV
n
01:40
nicm
In reply to this message
Would be glad to test it 👍
🤝
Л(
01:44
Out of curiosity, would MDBX_NOLOCK (if it existed) and MDBX_EXCLUSIVE be technically orthogonal/separate things?

I wonder if a combination of those two would solve the problem. I imagine MDBX_NOLOCK would make sure txn can be aborted on a different thread (on all OSes), while MDBX_EXCLUSIVE would solve the problem I mentioned earlier about LMDB/MDB_NOLOCK: other processes trying to access the file would just get an error instead of causing data corruption.
Л(
20:48
Леонид Юрьев (Leonid Yuriev)
Выпуск libmdbx v0.12.10 подготовлен, но ложку дёгтя добавил GitFlic.
https://gitflic.ru/project/erthink/libmdbx/release/158377dd-3d8c-4f2b-bbf0-a360ffd95e01
👍
СМ
🙏
AK
Л(
21:27
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Описание выпуска (вроде-бы) получилось привести в порядок. Телеграфируйте если заметите ошибки.
15 March 2024
Л(
01:35
Леонид Юрьев (Leonid Yuriev)
Тут вот в offline снова спросили "Почему MDBX так плохо показала себя в тестах RAIDIX ?"

Речь о докладе RAIDIX на каком-то Highload++ (несколько лет назад) по поводу их теста key-value движков в сценариях хранения метаданных СХД (систем хранения данных).
По мотивам этого доклада есть статья на Хабре.

Краткий ответ на вопрос = эксцесс исполнителя (бенчмарк выполняли некомпетентные студенты), поэтому результаты недостоверны.
Разбирать же все ошибки и странности тут нет ни смысла, ни возможности, поэтому только три вещи, которые помню:

1. Скорее всего исполнители не учли эффект кэша файловой системы, а именно что ядро может выделить под него всю свободную память, совсем не обращая внимание на ограничение использования ОЗУ для работающего с БД приложения.
В результате, движки читающие данные с диска через файловый ввод-вывод. неявно использовали под кэширование всю доступную системе память.
Косвенно это подтверждается тем, что RocksDb показывал latency на случайном чтении меньше задержек диска даже когда объем БД был сильно больше заданного ограничения на исопльзования ОЗУ, но при дальнейшем увеличении объема все резко стало тормозить.

2. В сценарии мелких точечных обновлений для MDBX latency доходила до ~100 секунд на NVMe диске.
В принципе не понятно как такое может быть на исправном "bare metal", так как штатная (не отладочная и т.п.) сборка libmdbx в таком сценарии обновляет/пишет на диск вполне определенное количество страниц (чуть больше чем высота b-tree, обычно это меньше 10).

3. Из не вызывающего сомнения там совершенно понятный/прозрачный выигрыш LSM-движков в сценариях мелких частых апдейтов и/или короткоживущих данных.
👍
N
VS
🔥
VB
VS
04:13
Victor Smirnov
Это да. Если хочется предсказуемости, то надо либо вообще отодвигать ядро в сторону (SPDK), либо его тонко настраивать. А это сразу другой класс решений, начиная с того, что придется использовать файберы, которые ни с чем высокоуровневым не совместимы. Т.е. про встраиваемый режим придется забыть.
Л(
04:23
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Ох, Виктор, там всё намного-намного проще...
Если нет навыка/привычки к студентам, которые в среднем находчивы и чудесно умеют "за уши" притягивать любые результаты к любой теории, то все графики и результаты в оном докладе/статье могут показаться вменяемыми.
Соответственно, 99% народа таких навыков не имеет и, в целом, не тяготеет вчитываться, вникать и, тем более, рассуждать.
Поэтому этой фигней (статьей/докладом) меня кто-нибудь "парит" примерно раз в квартал ;)
😁
i
VS
05:11
Victor Smirnov
In reply to this message
Почитал статью. На этом места взоржал как конь гомерическим хохотом:

Теперь можно обновить список выводов:

Запись + мало потоков => WiredTiger
Запись + много потоков => RocksDB
Чтение + DATA > RAM => RocksDB
Чтение + DATA < RAM => MDBX
Много потоков + DATA > RAM + Упаковка => Aerospike


Сорри, если тут кто из авторов есть. На LSM чтение медленное, по дизайну. Там не просто бинарный поиск, там еще и поиск по большому количеству сегментов, число которых может достигать O(N) от количества ключей. Эта структура данных деградирует по чтению, если запись быстрее, чем слияние сегментов. Причем деградация ухудшается еще и тем, что сборщик мусора в таком режиме съест вест BW у SSD. И она ведет себя очень плохо, когда данные не помещаются в кэш. Теоретически — гораздо хуже btree-like.

Т.е. у неё в какой-то прекрасный момент проблемы начинают нарастать как снежный ком. К счастью, она способна "само-исцеляться", нужно только или подолждать или докинуть узлов в кластер. Поэтому, она вполне подходит для эластичных облаков. Но в итоге эти все сложности выливаются в то, что люди просто резервируют избыточные мощности под БД. Что убивает весь смысл этой структуры данных.
05:12
А так, у меня Cassandra выруливала в таких экстремальных ситуациях, что ни в сказке сказать, ни пером описать. Железо — уровень смартфона 2014-го года. Дохла-пухла, но данные принимала, а потом отдавала.
05:17
Но когда я начинаю такие военные истории рассказывать людям уровня cloud architect (полученный в остальном вполне заслуженно), то мне никто не верит. Как это, всеми боготворимый RocksDB может быть нестабильным? Апелляции к теории не имеют смысла, так как перебиваются практикой "у меня всё всегда работало, чего ты гонишь?".
Л(
05:23
Так вот, про тестирование БД. Их надо тестировать не только в "рабочем режиме" (даже этого не делают!), что подразумевает, что известно распределение запросов. Но так же и в граничных режимах, что предполагает знание внутренних механизмов работы и режимов устойчивости. У того же RocksDB на последнее довольно легко напороться. Если на вставке 200-байтных записей она быстра, как молния, то на 4K уже почему-то сборщик вовремя не справляется, и запись сильно проседает (на порядок).
05:29
LSM быстры на запись и незаменимы в своей нише, там, где надо быстро принимать всплески трафика, но между всплесками будут периоды низкой активности, за которые сборщик успеет сделать своё дело. Это обычно — веб-магазины и прочие веб-сайты, которые время от времени испытывают резкий приток посетителей. Поэтому LSM в вебе незаменимы. А не потому, что они "самые быстрые". Они, да, быстры в микробенчмарках, которые либо выполняются быстро (и проблема GC не возникает), либо создают стационарную нагрузку, к которой GC иногда способен адаптироваться.
05:31
А тесты уровня "У нас RocksDB бастрее всех, давайте его выберем" — это, пардон, десткий сад.
05:32
То же самое — про Aerospike.
05:42
Но это, как я уже сказал, может быть уровень архитектора в вашей (это я тут ко всей аудитории обращаюсь) компании, поэтому настоятельно не рекомендую вот эти мои аргументы приводить на планерках. Вас могут уволить))
👍
N
Л(
Л(
05:47
Леонид Юрьев (Leonid Yuriev)
Ну, всё-таки, в упомянутой мной докладе/статье, начинается (или заканчивается) все еще проще - не совсем понимали "как работает", "крутили что увидели" и смотрели на "приборы", на которые вздумали смотреть.
Увидели "42", собственно "занавес" ;)
VS
05:49
Victor Smirnov
Именно)
05:50
Вам, Леонид, там можно свой бизнес открывать по консультированию в области архитектур систем хранения и обработки данных. Рынок хоть и маленький, но совсем пустой в плане наличия специалистов))
Л(
06:00
Леонид Юрьев (Leonid Yuriev)
In reply to this message
99% проблем в области "этой группы", физики, экономики и т.д. решаются путем "взять исходные данные/знания, отфильтровать и подумать", но после ещё нужно и "отдать фотографию" (вспоминая Простоквашино и дядю Федора).
Некий феномен нашего времени, как мне кажется, в том что на "отдать фотографию" может уходить до 80% усилий.

В любом случае, спасибо за ободряющие комплименты.
06:00
И пардон, пора поспать.
VS
06:01
Victor Smirnov
Короче, я тут опять кучу всего написал. TLDR. Понимание как базовых, так и граничных режимов работы как MDBX (LMDB, кто из США), так и RocksDB — обязательно, кандидатский минимум. CoW B-tree — на много более стабильная структура данных, поддерживает транзакции из коробки, и очень быстра на чтение. LSM отлично переваривает всплески по записи, но ценой всего остального.
06:02
In reply to this message
Дорой ночи!)
16 March 2024
n
20:40
nicm
In reply to this message
Just to clarify, I wasn't asking for a MDBX_NOLOCK feature, but just asking out of curiosity for my own info. (If my question was too stupid, sorry.)

Anyway, I look forward to the "MDBX_NOTLS for write txns" feature. (This would become a new use case that LMDB simply cannot do in a "it just works" way.)
19 March 2024
Л(
17:27
Леонид Юрьев (Leonid Yuriev)
Копирую вопрос-ответ их других каналов/потоков.
Позже, возможно, скопирую в раздел вопросов-ответов в документации.

> Добрый день, я изучаю samba ad и там используется lmdb подскажите насколько база mdbx
> быстрее чем lmdb и проводились ли какие либо тесты с этими базами? так как замечаю что
> lmdb на больших объемах данных уже не такая быстрая)

MDBX является глубокой переработкой LMDB, поэтому в среднем немного быстрее. Но это "в среднем немного быстрее" принципиально сильно зависит от сценария использования:
- В тривиальных сценариях MDBX может быть чуть медленнее, ибо производит существенно больше проверок параметров и корректности использования API, целостности БД и внутренних структур.
- В большинстве обычных сценариев разница не заметна (на уровне погрешности измерений), так как основное время затрачивается на обмен с диском (за вычетом времени ожидания диска MDBX может быть в разы быстрее, но как правило разница в 5-20%).
- В ряде специфических и/или экстремальных сценариях MDBX может быть в тысячи раз быстрее LMDB. Поэтому MDBX сейчас является основным движком хранения в Ethereum (Erigon, Reth, Silkworm, Akula).

Однако, MDBX как и LMDB, использует отображение в ОЗУ для доступа к данным и B-дерево без WAL в качестве базовой структуры БД. Специалистам этого достаточно чтобы предсказать все слабые и сильные стороны:

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

Принципиально важно, что если горячих данных (к которым обращение происходит часто или регулярно) не много и они помещаются в доступное ОЗУ, то накладные расходы на доступ к данным стремятся к нулю. В таких сценариях производительность MDBX выше практически всех других движков хранения. Точнее говоря, схожую производительность показывают все движки хранения использующие отображение данных в ОЗУ, и эта производительность примерно равна std::map<> в C++. В случае MDBX производительность в этом случае хорошо масштабируется по ядрам ЦПУ (по потокам и/или процессам) и ограничивается шириной пропускания ОЗУ.

Напротив, если данных существенно больше объема доступного ОЗУ, то большинство обращений к данным (включая поиск по В-дереву) будет требовать чтения данных с диска, которое в случае отображения в ОЗУ выполняет через механизм подкачки: при обращении к отсутствующей в памяти страницы БД происходит прерывание/исключение, чтение данных (одной или 2-4-8 соседствующих страниц) с диска, изменение таблиц PTE, переключение обратно из режима ядра в режим пользователя. В сравнении с файловым вводом-выводом накладных расходов тут сильно больше, а чтение с диска происходит как правило меньшими порциями. Масштабирование по потокам (по ядрам ЦПУ в рамках одного процесса/приложения) тут очень плохое, так как все запросы на подкачку страницы ядра всех ОС сейчас выполняют строго последовательно.

Таким образом, в сценариях чтения, производительность MDBX (и LMDB) принципиально/кардинально зависит от соотношения объема рабочих/горячих данных в БД и объема доступного/свободного ОЗУ (которое ядро может использовать для кэширования). Отображение в память тут действительно имеет свои минусы, которые обязательно нужно учитывать. Тем не менее, движки отображающие данные в ОЗУ, достаточно популярны по двум причинам:
- всё управление кэшированием происходит автоматически, при этом ядро ОС само использует всё доступное ОЗУ и балансирует его использование между всеми приложениями и подсистемами.
- работает принцип «хочешь быстрее — нарасти ОЗУ», без каких-либо заморочек.
СО
17:27
2.
Отсутствие WAL (журнала транзакций и т. п.) означает что у БД отсутствует фаза восстановления после системных сбоев, но зато больше объем дисковых операций при фиксации транзакции. Например, в сценариях точечного вставки/обновления/удаления пары key-value, при фиксации транзакции с WAL в большинстве случаев требуется записать на диск один сектор, а без WAL количество страницы равное высоте B-дерева (3-10 страниц).

Кроме этого, есть ряд сценарием с преимущественно коротко-живущими данными, когда добавленные элементы вскоре удаляются, либо недавно добавленные/обновленные элементы обновляются чаще чем старые. Тут всегда будет выигрывать LSM.

Поэтому в сценариях «много мелких и частых обновлений» MDBX (и LMDB) будут в среднем уступать движкам с WAL, особенно на «бытовом» и low-end оборудовании с низкой производительностью записи на носитель/диск. Но опять-таки, много зависит от деталей/тонкостей сценария использования:
- С увеличением размера данных и/или с увеличением объема (кол-ва операций в транзакции) баланс смещается и движки с WAL могут показывать себя хуже, так как кратно увеличиваются накладные расходы.
- Если нагрузка по-чтению выше чем по-записи, то отсутствие WAL также может быть более выгодным, особенно в сценарии конкурентной работы с БД несколькими процессами.

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

Если говорить про AD, то это так называемый «LDAP сценарий», когда данных не много, темп и объем изменений не велик, и запросов на чтение существенно больше чем на обновление/запись. Это целевой сценарий для которого создавалась LMDB, соответственно он является «золотым» и для MDBX. Поэтому MDBX и LMDB, как правило, либо существенно опережают все другие движки хранения, либо делают эксплуатацию более простой/комфортной и надежной (да, несмотря на обилие ошибок/глюков в LMDB, работа с каким-нибудь BDB может быть проблемной из-за еще большего кол-ва багов, а PostgreSQL может быть неприемлемо плох из-за слоя отображения модели данных LDAP/AD на таблицы и SQL).

Даже если объем каталога существенно больше ОЗУ, то в LDAP/AD сценарии MDBX и LMDB могут выглядеть кардинально более предпочтительными по совокупности свойств/характеристик, удобству настройки/эксплуатации и т. п. В самом простом пояснении, всё работает по принципу «поставил, включил и забыл», а если медленно «то просто добавь ОЗУ».

Для меня достаточно очевидно и не вызывает сомнения, что в Samba AD использование MDBX вместо LMDB, точно добавит надежности и удобства пользователем (хотя-бы из-за возможности автоматического управления размером БД). Но протолкнуть подобные доработки в Samba AD сейчас не представляется возможным (только если записаться в чернокожие трансгендерные лесбиянки, заклеймить Путина и РФ и т.п.).
🔥
VB
VS
17:36
Victor Smirnov
In reply to this message
Это для всех операционных БД справедливо, без иллюзий. Некоторые чуть быстрее деградируют, некоторые — чуть медленнее. Но деградируют — все. Ну, кроме тех, которые делают только линейное чтение, типа очередей (кольцевых буферов).
Л(
17:48
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Отвечаю на всякий случай, для прояснение и взаимопонимания.

Иллюзий действительно нет, точнее не должно быть.
С ростом объема ОЗУ некая деградация неизбежна, как минимум только потому, что растут затраты на обновление статистики по использованию страниц.
К этому нужно добавить (а в худшем случае домножить на) количество page faults с последующим поиском и вытеснением холодных страниц.

В крайних/экстремальных случаях чрезмерное увеличение ОЗУ может приводить к заметной деградации общей производительности системы (заметное в виде кардинального увеличения system time в статистике процессов).
Сам видел что-то подобное, но очень давно, еще в бытность FreeBSD 2.x/3.x, ну и много подобных баек было на заре становления Интернета.
VS
17:48
Victor Smirnov
In reply to this message
Тут важен не размер БД, а размер рабочего множества. Трудно представить, что приложения LDAP одновременно запрашивают ключей на десятки GB)))
17:50
In reply to this message
Да-да, про иллюзии — это я просто для аудитории сказал. Чтобы не покупалась на обещания "LSM будут лучше если БД сильно больше RAM". Не будут)
👍
Л(
Л(
17:54
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Конечно, это очевидно.
Но (увы) не все задающие вопросы понимают что такое рабочее множество и т.п.
Кроме этого, в LDAP несложно зафигачить запрос приводящий к fullscan.
Что характерно, в случае MDBX/LMDB проблемы из-за fullscan-ов начинают замечать часто именно когда БД перестает помещаться в ОЗУ, ибо пока помещается даже десятки гигабайт пережовывается достаточно быстро.
VS
17:57
Victor Smirnov
In reply to this message
Fullscan на современных SSD, кстати, — это, практически, RAM. Лишь бы программный стек мог этот поток данных поднять.
Л(
17:58
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Это если io_ring, а в случае memory mapped достаточно грустно.
VS
17:59
Victor Smirnov
Кстати, когда Mithril будет?) Сейчас — золотое время для БД, но оно золотым не навсегда будет))
18:00
In reply to this message
SSD подороже даже этот плохой сценарий неплохо вытягивают.
n
18:39
nicm
"(только если записаться в чернокожие трансгендерные лесбиянки, заклеймить Путина и РФ и т.п.)." 😀👌👍
AA
19:36
Alexey Akhunov
In reply to this message
а с чем связано это золотое время для БД? С ажиотажем вокруг "искусственного интеллекта" (типа нужно все эти входные данные где-то хранить)? или с чем-то ещё?
VS
20:03
Victor Smirnov
ИИ создал практически неограниченный спрос на compute, что сейчас приводит к радикальному пересмотру вычислительных архитектур. И базы данных вместе со storage тут в самом центре событий находятся. Это еще не видно в уютненьких офисах веб-девелоперов, осваивающих новые API и фреймворки, а тем временем, образно говоря, в подземельях гномов, кующих железо, уже кипит работа))
20:10
Вот, например, я сейчас прорабатываю вопрос о том, чтобы частично перенести LMDB-подобную схему управления данными непосредственно в контроллер NVMe SSD. Это (в сочетании с другими аппаратными решениями) позволит снизить задержки на фиксацию транзакции и поднять общее быстродействие. Такое устройство будет общаться с внешним миром уже не по NVMe, а по RPC-подобному протоколу и сможет выполнять несложные запросы прямо на контроллере. В результате — минус много толстых и ненужных слоев, и к устройству можно будет обращаться прямо из аппаратной логики SmartNIC (у которого задержки — микросекундные), минуя CPU.
👍
i
f
5
🤔
N
20:12
Такая программно-аппаратная схема будет интересна, например, для организации узла персистентной очереди (a-la Kafka) с низкими, микросекундными, задержками.
20:15
"БД в контроллере NVMe SSD с доступом по RPC" — это звучит сейчас дико, я понимаю. Это здесь просто фрагмент более общего фреймворка, в рамках которого оно начинает иметь системный смысл.
VS
20:36
Victor Smirnov
Вот есть компания, которая делает специальные DDR4 DIMM со встроенными в банки памяти процессорами. На логику в DRAM накладывается большое количество ограничений, но этим гениям удалось впихнуть аж целый процессор. В результате, kernels можно выполнять прямо в DRAM с агрегированной пропускной способностью в 1TB/s+.
20:41
Задержки DRAM приметно 15нс, а когда данные доезжают до ALU CPU, они превращаются в 100нс (из-за кучи кешей, контроллеров и clock boundaries). Если код последовательный и ему нужны низкие задержки, то такие вычисления пойдут как можно ближе к данным, алгоритмы будут превращаться в сложный dataflow (обмен сообщениями).
20:41
Раньше это всё представляло больше академический интерес и на практике востребовано не было, но теперь времена поменялись.
20:44
Нейросетевой ИИ — это другая песня, у них там высокая интенсивность обработки, и тут лучше тащить данные в акселераторы и там их молотить до потери пульса. Но GenAI парадоксальным образом создает аномально высокий спрос и на весь остальной compute (тот же GOFAI), что приводит нас к базам данных. Которым теперь придется искать себе новое место в новом мире))
VS
21:13
Vladislav Shchapov
In reply to this message
Подскажите, пожалуйста, а как модель работы с данными в очереди ложится на LMDB-подобую схему?
VS
21:18
Victor Smirnov
In reply to this message
1. В персистентной очереди. Для просто очереди в памяти LMDB не нужна.
2. Тут я предполагаю, что есть достаточные навыки, чтобы или добавить в LMDB нужный функционал, или переписать этот LMDB заново, по-нормальному.

B+tree позволяет "дописывать в начало/конец", и удалять из. Можно писать в середину — очередь с приоритетами получится. Транзакции на запись/удаление сериализованы. Аналитика (сканирование очереди) не влияет на писателей.
VS
21:20
Vladislav Shchapov
In reply to this message
Я понял идею! Спасибо за разъяснения.
AA
21:26
Alexey Akhunov
@benevolent_bot Благодарю за разъяснения
20 March 2024
n
12:03
nicm
With LMDB, just duplicating a database (iterating through it and writing key-value pairs to a new database) can massively reduce file size. Is this also the case with MDBX / can MDBX automate this more in some ways?
DH
12:18
Dvir H
In reply to this message
Sorry about the beginner question, why are all OS kernel page swap requests performed sequentially in case a database is bigger than RAM?
Л(
12:22
Леонид Юрьев (Leonid Yuriev)
In reply to this message
The DUPSORT feature is the approach when multiple values associated with the same key are stored in nested b-tree.
Such feature is basic of LMDB, and MDBX inherited it with minor refinements.
Л(
12:38
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Such serialization is traditional implementation of most implementations of virtual memory paging since VMS on VAX.

In general, a thread accessing a non-existent page triggers an internal hardware interrupt of the processor, which is handled by the operating system kernel.
Thus, this thread is blocked without any alternative until the page-in operation will completed with a return from the interrupt handling procedure.
However, if other threads also access the missing pages, then in most implementations they will wait either for the process's PTE-table lock, or on the I/O queue.
This approach solves|simplifies many difficulties, for example, handling cases when another thread accesses the page for which the page-in is performed already, or page-out was scheduled, etc.
👍
DH
LP
AS
13:36
Alex Sharov
In reply to this message
There is some setting “page-merge threshold”. if increase it - it will make table more dense.
13:39
In reply to this message
And there is FreeList - “copy data to new db” means “abandon FreeList”. MDBX has some settings to Coalesce freelist and even return some pages to OS and shrink file. But it’s probabilistic and freelist likely will take space. You can try to reduce write transactions size and read transactions time - to reduce freelist.
Л(
13:43
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Кстати, в devel-ветке уже есть:
- запрошенное в январе API для получения информации от БД без открытия;
- опция для уменьшения резервирования на вложенных LEAF2-страницах (вроде-бы как-то давно ты подозревал там неладное);
- опция смена тактики слияния страниц при перебалансировки для уменьшения WAF ценой менее равномерного заполнения страниц.

Но опции пока только через правку кода.
AS
13:45
Alex Sharov
In reply to this message
Что такое WAF?
Л(
13:46
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Write Amplification Factor.
Проще говоря - количество страниц, которое записывается при фиксации транзакции.
AS
13:47
Alex Sharov
Спасибо
Л(
13:56
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Собственно в одном из недавних коммитов поправлена тактика выбора соседней страницы при слиянии.
Там еще есть над чем подумать, так чтобы не усложнять логики и набор опций.
Но суть в выборе между правой и левой соседней страницей для переноса элементов из текущей страницы, когда она почти пустая:
- если обе (правая и левая) страницы либо "грязные", либо еще не обновлялись в транзакции, то для слияния однозначно выгоднее выбрать менее заполненную.
- но когда "грязная" только одна, мы может либо выбирать её (даже когда другая нетронутая страница менее заполнена при этом не увеличивая WAF), либо выбирать менее заполненную страницы (этим обеспечивать более равномерное заполнение, но увеличивать WAF).

Было-бы любопытно посмотреть как поменяется поведение на ваших гигатера-БД и мегагига-транзакциях.
AS
14:09
Alex Sharov
In reply to this message
А какая стратегия более friendly для случая - сортированная пачка апдейтов?
14:14
Я помню например - когда page-split делали - понадобилось специальные сценарии для обновления в конце/начале страницы добавить - тогда стало - pre-sorted batch update friendly.
Л(
14:23
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Если данных (рабочие множество) сильно больше чем ОЗУ, то теоретически должно быть выгоднее уменьшать кол-во страниц, а для этого заполнять их более равномерно.
Но если узким место все-таки является запись на диск при фиксации транзакции, то лучше уменьшать WAF в ущерб равномерности заполнения.

Однако, на это накладывается некий реальный паттерн вставок/удалений, который в большинстве случаев имеет некий "узор" (не является полностью случайным с равномерным распределением).
Буквально, при выборе тактики уменьшения WAF страницы будут менее сбалансированными, поэтому картина принципиально зависит от того, как много последующих удалений и вставок придется на менее заполненные страницы.

Короче, проще попробовать ;)
n
14:24
nicm
In my example, a 220GB LMDB database was reduced to 120GB just by duplication.

I was wondering if it's possible (in LMDB or MDBX) to do this reduction "in-place" (without creating a new database).
Л(
14:34
Леонид Юрьев (Leonid Yuriev)
In reply to this message
There are definitely no miracles, including you should not overestimate the zero-cost auto-compactification in MDBX.
Please just dig and read how it works.
👌
n
i
20:39
igors
In reply to this message
👍
VB
21 March 2024
Л(
12:03
Леонид Юрьев (Leonid Yuriev)
Ветка devel влита в master.
Краткое и пока неполное описание нового функционала и доработок см в ChangeLog.md.

В ближайшее время я сосредоточусь на документировании, после чего постараюсь добавить аналог режима NOTLS для пишущих транзакций (полная отвязка транзакций от потоков).
Выпуск версии v0.13.1 намечен на 9 апреля.
VB
3
👍
N
VS
n
15:41
nicm
I wonder why most databases constrain transactions to a thread. (My own blind guesses: To "save the users from their own mistakes"? Because OS level mutexes are somehow more performant?)
AV
15:55
Artem Vorotnikov
In reply to this message
Because the standard Linux mutex mechanism requires unlocking from the same thread
Л(
16:00
Леонид Юрьев (Leonid Yuriev)
In reply to this message
s/Linux/POSIX/
VS
16:03
Victor Smirnov
In reply to this message
And constrain transaction to a single thread. It will slow down everything dramatically otherwise. If you need more parallelism, start more transactions.
n
16:49
nicm
Yes but I wonder, why did Linux/POSIX have this limitation for mutexes to begin with? Maybe some history buffs here know.

(If this limitation wasn't there, I guess most programs would still be fast because most programs keep a mutex on a single thread anyway -- and only rare programs that use mutexes across theads would slow down.)
Л(
17:07
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Ohh, please RTFM about the POSIX.

There many POSIX versions are.
The "Shared Mutexes" (aka IPC mutexes, which could be placed in a shared memory) was defined/standardized by POSIX-2001.
Then the "Robust Mutexed" (the EOWNERDEAD with system-side recovery) was defined/standardized by POSIX-2008.
17:07
However, many operating systems still do not support current versions of POSIX. For example, macOS/OSX (based on Mach kernel) does not support shared mutexes, and many other systems do not support robust mutexes (include non-modern versions of glibc).
👌
n
22 March 2024
mocius invited mocius
n
13:42
nicm
Thanks for summarizing this for this mere mortal.

Interestingly, it seems C++17 has shared_mutex. Not sure if anyone made it for C too (cross-platform).

Anyway, I look forward to seeing how your solution will look (not that I'd fully understand the low-level details). I'll leave you geniuses alone for now and wish you a great Friday/weekend!
СМ
13:44
Сергей Мирянов
In reply to this message
it is not that shared mutex that Leonid talked about
👌
n
Л(
13:48
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Thanks.

The std::shared_mutex is for shared/exclusive locking, aka "rd-wr" (read-write lock), i.e. multiple readers (shared lock) either single writer (exclusive).
There nothing about sharing mutexes between processes.
👍
n
24 March 2024
Л(
12:36
Леонид Юрьев (Leonid Yuriev)
@AskAlexSharov, вы пробовали новую ветку 0.13.x, то что сейчас уже в master ?
Есть какая-то информация, либо когда сможете попробовать ?
AS
12:37
Alex Sharov
In reply to this message
Попробовал, поймал segfault, но не запускал со врубленными ассертами
Л(
12:38
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Ну вот мне как-раз нужно подобные глупые ошибки отловить, которые проявляются в специфических сценариях, но не в тестах.
12:40
Тесты сейчас показывают отличную стабильность, есть только недочеты в gc_update() после доработок.
Думаю на днях дополировать.
AS
12:45
Alex Sharov
Лежу болею :-( попробую че-нить запустить
Л(
12:51
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Ну тогда лучше выздоравливай и маякни когда поймешь что сможешь работать.
Как минимум смотри по самочувствию.
Л(
13:15
Леонид Юрьев (Leonid Yuriev)
Начиная с версии 0.13 библиотека будет распространяться с дополненной лицензией.
Основное изменение связано с запретом на использование и распространение исходного кода не в целостном виде (с удалением части файлов), а также распространение производного бинарного кода без информации об использовании библиотеки и/или без её файла лицензии.
👍
LP
VS
14:05
Vladislav Shchapov
In reply to this message
Просто для уточнения, получается, что теперь нельзя будет использовать библиотеку со своими патчами? (это вытекает из запрета использования с изменениями исходного кода)
И нельзя будет распространять свои патчи?
Л(
14:12
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Нет, к патчам это отношения не имеет, пока они удаляют файлы целиком.

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

+++
Наверное проще объяснить отталкиваясь от цели — у пользователей (включая конечных пользователей производных продуктов) должна быть информация об используемом компоненте, его происхождении, последних изменениях, месте расположения проекта и т.п.

+++++
В том числе будет недопустима ситуация, когда из исходников и сопутствующих файлов удаляются README и все упоминания о Positive Technologies (основной донор и фактически материнская компания с 2016) по причине санкций и т.п.
👍
VS
VS
14:21
Vladislav Shchapov
In reply to this message
А! Цель понятна.

Только вопрос в том, как не поломать лицензию для тех, кто распространяет производное произведение только в бинарном виде.

Запрет использования модифицированных исходников сильно повышает риски при таком варианте использования. (если явно не сказано, что речь только о копирайтах).

С требованием ссылки на проект, например, в документации, я не спорю. Оно нормально.
Как и остальное.

На мой взгляд только «использование» в такой формулировке несет риски для разработчиков производных продуктов.
14:23
Грубо говоря, если был найден баг, то чтобы его пофиксить у себя, пока новая версия libmdbx еще не вышла, каждый раз придется проводить сверку этих изменений с условиями лицензии.
Л(
14:26
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Технически, скорее всего, будет требование прикладывать некий один файл, с лицензией и сопутствующей инфой.
К старым версиям это не будет иметь отношения, но туда будут портироваться только исправления багов.
А с 0.13 будет новая лицензия с новыми требованиями.
👍
VS
27 March 2024
Л(
21:40
Леонид Юрьев (Leonid Yuriev)
Завтра, при содействии Константина Ратвина, планирую немного помучить студентов МВТУ
https://disk.yandex.ru/i/Nn80-bt3JeieAQ
👍
i
?
A
5
A
21:55
Aртём
In reply to this message
Geth слезает с LevelDb на Pebble. Ремарка.
?
21:56
𝚂
In reply to this message
А где это будет?)
Л(
22:08
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Буду иметь в виду, но на днях специально смотрел исходники и видел leveldb.
Видимо Pebble в планах и/или в разработке.
22:09
In reply to this message
В online, точнее мне сказать сложно.
A
22:10
Aртём
In reply to this message
Не не там можно при старте Getha выбрать pebble или levelDb. Просто левелдб уже как легаси, идёт переход на pebble.
28 March 2024
15:25
Через 5 минут
i
15:37
igors
@erthink включите запись
15:38
чтобы потом посмотреть
Л(
16:01
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Мопед не мой, но запись будет.
👍
A
VS
16:06
In reply to this message
Ну вы не сильно облизывайтесь, там просто микро-обзор, чтобы студенты могли понять интересно им это или нет.
БЕЗ какого-либо погружения внутрь и/или технологического "мяса".
1 April 2024
𓂭𓃇 invited 𓂭𓃇
3 April 2024
James | Pangea invited James | Pangea
4 April 2024
Л(
18:22
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Please check out the MDBX_NOSTICKYTHREADS.
n
21:18
nicm
In reply to this message
Thanks.

"Write transactions can only be completed in the same execution thread where they were started."

So in the end, the issue (of not being able to abort write txn on a different thread) turned out to be unsolvable?
Л(
21:23
Леонид Юрьев (Leonid Yuriev)
In reply to this message
This is requirement most of OSes, i.e. the IPC-shared mutex used to serialize write transactions must release the same thread that acquired it.
Only Linux has an non-portable "fast mutexes" (actually a futex) allow such trick.
👌
n
Л(
22:08
Леонид Юрьев (Leonid Yuriev)
Nonetheless, in fact it is more complex since depends on MDBX_LOCKING and target OS.
For instance, an aborting write-txn from another thread is possibe when MDBX_LOCKING == MDBX_LOCKING_POSIX1988 (with POSIX-1 Shared anonymous semaphores).
But it is impossible on Windows, since now MDBX uses SRW-lock to synchronize DB resize, etc.

So I decided not to complicate and introduce a requirement/limitation that will ensure the same behavior on all operating systems, as well as simplify debugging and maintenance.
However, it is possible to someone will provide/support a custom build with small patch, etc.

// @nicholasm28, fyi
👌
n
n
22:35
nicm
You wrote about Windows being "obsolete" (saw it in the docs somewhere), maybe this is one more example 🙂

But it makes sense to keep this cross platform. Thanks for your efforts anyway. 👍
🤝
Л(
6 April 2024
Л(
17:21
Леонид Юрьев (Leonid Yuriev)
Камрады, я хочу попросить адекватной помощи по части донесения информации, особенно для опровержения ложного негатива.

Истории примерно такие:
- в knot не перешли на mdbx из-за утечек памяти, предположительно не читали readme и не добавили освобождение курсоров после lmdb;
- в deno и где-то еще запускали бенчмарки под windows (жуть!) и до массы доработок в 0.12.x для ускорения windows-мопеда;
- "тормоза" в solar-soc, там вообще дело в python-коде;
- несколько других мелких историй где что-то было понято и/или сделано принципиально не верно, включая случай с Miranda NG (но там были и мои ошибки).

Моя позиция проста и прозрачна:
- Всегда есть шанс "последней" ошибки, особенно в редких сценариях мимо тестов.
- MDBX разрабатывается и поддерживается для использования в продуктах Positive Technologies, в прочих случаях нужно пологаться на собственные тесты;
- все проблемы/ошибки исправляюся очень оперативно, в разы быстрее многих схожих проектов.
👍
A
b
i
8
AV
19:26
Artem Vorotnikov
А где-то конкретно набросили на вентилятор?
10 April 2024
Л(
14:36
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Пардон, отложил и забыл ответить.

По текущей информации, в основном в личной переписке или без фиксации (на конференциях/митапах, не под запись или в кулуарах).
13 April 2024
Л(
11:40
Леонид Юрьев (Leonid Yuriev)
Выпуск v0.13.1 был запланирован на 9 апреля, но пока откладывается из-за одного не устраненного регресса.

Были сделаны доработки кода обновляющего GC при фиксации транзакции, и пока тут остаётся проблема = в редких ситуациях возможен возврат ошибки MDBX_PROBLEM (-30779).
Последние дни тестировалось исправление, но сегодня утром получен отрицательный результат.

Даже если у меня получится устранить проблему сегодня-завтра, то выпуск будет не раньше конца Апреля, так как последующее тесты для уверенности должны отработать хотя-бы неделю, а лучше две.
Тем не менее, для разработки рекомендуется использовать именно ветку master.
👍
VS
VS
14 April 2024
b
22:16
basiliscos
Здравствуйте. Добавил mbdx как подпроект в cmake (add_subdirectory("lib/mbdx")). Как его попросить не перебилживаться каждый раз если поменяется внешний cmake, никак не связанныый с изменениями?
Л(
22:36
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Для libmdbx предусматривается/предполагается два режима интеграции:

1. Режим пользователя библиотеки.
В этом случае необходимо взять файлы из архива с конкретным выпуском/релизом, либо на linux-машине выполнить make dist внутри директории с локальным клоном git-репозитория и взять фалы из подкаталога ./dist.
В этом случае в вашем проекте не будет лишних файлов, в том числе не будет собственных тестов libmdbx.
При этом информация о конкретной версии библиотеки внедряется в исходники.

2. Режим контрибьютра и/или тестировщика.
Это когда вы подключаете целиком git-репозиторий как git-submodule и/или под-директорию/под-проект cmake.
В этом случае с сборку будет включено все "внутренности" libmdbx, в том числе тесты.
При этом информация о версии (включая git-tree-hash и git-commit-hash) будет формироваться/обновляться при каждой сборке.

- - -

В вашем случае, видимо, всё "совсем плохо". Вы интегрировали libmdbx не как cmake-subproject, а как cmake-subdirectory.
Соответственно, cmake-сценарий внутри libmdbx обновляет информацию о версии при любом коммите в вашем основном проекте.
Выхода примерно два: (а) перейти на первый из описанных выше способов интеграции, (б) использвовать git-submodule + cmake-subproject.
b
22:50
basiliscos
а есть где-то пример (б) git submobmodule + cmake subproject?
22:51
ну т.е. не хотелось бы отдельно что-то инсталлировать и т.п.
22:53
в идеале, конечно, хотелось бы conan2 рецепт в центральном репозитории )
Л(
23:00
Леонид Юрьев (Leonid Yuriev)
Инсталлировать вам ничего не надо.
По make dist будет сформирован набор файлов, которые вам достаточно просто добавить в ваш проект, можно прям как сейчас в lib/mdbx.
Соответственно, обновлять библиотеку вы будите сами по необходимости - обновите те же файлы и закоммитите.

По поводу git-submodule и cmake-subproject просто погуглите и RTFM.

Рецепта conan в центральном репозитории никогда не будет, так как этот репозиторий находится на github, а в свою очередь github один раз уже заблокировал доступ и поэтому blacklisted forever.
23:02
Поддержка Conan появиться как только GitFlic реализует свой conan-репозиторий.
На мой вопрос о поддержка Conan от CTO GitFlic было устное озвучивание планов сделать это.
👍
b
b
23:04
basiliscos
In reply to this message
это неудобно, т.к. доп. телодвижения делает для сбоки проекта. Хотелось бы

git clone ..
cd my_prject
mkdir build
cd build
cmake ..
make


а так надо будет ещё куда-то

cd lib/mbdx
mkdir build
cd build
cmake ..
make dist


или что-то такое.
Л(
23:10
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Вы видимо что-то недопонимаете и/или неверно оцениваете.
Как минимум, чтобы делать выводы удобно/неудобно лучше иметь опыт работы с git-submodule и cmake-subproject, в том числе при долго поддержке проектов такой интеграцией и т.п.
Поэтому, при всём моём уважении, я не прислушаюсь к вашему мнению.

Советую хотя-бы просто попробовать оба варианта интеграции, в том числе освоить git-submodules и cmake-subprojects.
В любом случае, это точно полезные упражнения/знания/навыки.
b
23:17
basiliscos
может я как-то не так гуглю, но мне выкидывает ты всё время как "add_subdirectory", например сюда https://github.com/ttroy50/cmake-examples/blob/master/02-sub-projects/A-basic/CMakeLists.txt
23:17
т.е. интеграцию подпроектов советует как "add_subdirectory", а если я правильно понял, "так не надо интегрировать mbdx"
Л(
23:20
Леонид Юрьев (Leonid Yuriev)
23:21
In reply to this message
Все способы интеграции будут работать, но вы должны понимать что делаете, зачем и что при этом происходит.
b
23:22
basiliscos
In reply to this message
а-а-а, имелся в виду ExternalProject. Ок, спасибо, попробую. )
Л(
23:25
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Ваш исходный вопрос был о том, как избавиться от лишних пересборок libmdbx, в случае когда изменяется только что-то снаружи.

Так вот, при любом правильном способе интеграции лишних пересборок libmdbx не будет.
Но лишняя пересборка будет, если вы просто скопируете файлы из git-репозитория libmdbx, а не будете использовать для этого файлы от make dist или не будите использовать git-submodule или cmake-subproject.
👍
b
15 April 2024
DH
15:58
Dvir H
I see that the database format depends on the endianness. What will happen if I write to the database in a big-endian system and then try to open the same database in a little-endian system? Will the opening fail, or succeed and I may corrupt the database?
VS
16:06
Victor Smirnov
In reply to this message
Use dump/restore for moving DB between systems with different endianness.
👍
Л(
DH
17:11
Dvir H
In reply to this message
1. By restore, do you mean mdbx_load?
2. If I don't use dump and restore (I use the mdbx.dat file directly), will opening a database on a different system succeed? In that case, if by luck, all the verification of some get operation passes, I will get the wrong data?
Л(
17:27
Леонид Юрьев (Leonid Yuriev)
In reply to this message
The signature of database format is depend on endianess, as on other major options/properties.

So, if opening a db on a different system succeed, then it is the same endianess, etc.
👍
DH
19 April 2024
S invited S
20 April 2024
b
13:50
basiliscos
не знаю критично или нет

 In file included from /home/b/development/cpp/syncspirit/lib/mbdx/src/alloy.c:18:
/home/b/development/cpp/syncspirit/lib/mbdx/src/osal.c: In function 'osal_ioring_add':
/home/b/development/cpp/syncspirit/lib/mbdx/src/osal.c:704:24: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
704 | (uintptr_t)(uint64_t)item->sgv[0].Buffer) &
| ^


mbdx v0.13.0, варнинг появляется при компиляции 32-битным компилемром (i686-w64-mingw32-g++ ).
Л(
13:52
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Не критично, но поправлю.
Спасибо.
🔥
b
Л(
22:29
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Посмотрел внимательно.
Предупреждение выглядит несколько странным/ложным.

Дело в том, что item->sgv это массив из FILE_SEGMENT_ELEMENT, в котором поле Buffer имеет тип PVOID64.
Т.е. на самом деле там 64-битный указатель, даже при сборке для 32-битной целевой платформы.
Соответственно, нужен именно такой двойной кастинг и как-раз для того чтобы не было подобных предупреждений.
Причем если убрать кастинг, то подобное предупреждение будет от MSVC.

Итого:
- исходный код верный, исправлять нечего;
- возможно в вашем случае проблема в компиляторе и/или старом Windows SDK, либо в какой-то неувязке между SDK и компилятором;
- тем не менее, если предупреждение не ошибочное, то компилятор считает поле Buffer 32-битным, а результирующий код может неожиданно сбоить в специфических ситуациях (в каких-то сценариях при запуске 32-битного приложения на 64-битной Windows);
- можно попробовать "на всякий случай" поправить кода на ((uintptr_t)((uint64_t)(item->sgv[0].Buffer))), т.е. добавить скобочек чтобы четче увидеть подсветку в диагностике.
b
22:31
basiliscos
как я понимаю, uintptr_t 32-х битный на 32х битной платформе. Собтсвенно по этому и ругается?
Л(
22:35
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Ну так там поэтому сначала кастинг 64-битного указателя в uint64_t, а потом кастинг uint64_t в uintptr_t.

Именно в этом месте никаких проблем, в любом случае, не будет.
Но сам факт предупреждения говорит о том, что компилятор вроде-бы считает поле Buffer 32-битным указателем — а вот это уже проблема при запуске на 64-битной платформе (старшие 32 бита останутся не-инициализированными).
22 April 2024
Павел invited Павел
b
09:53
basiliscos
09:53
с этим можно что-нибудь сделать? Это при запуске под windows xp :)
09:55
выстреливает при mdbx_env_create()
b
12:20
basiliscos
@erthink есть ли шансы, что под winxp заведётся?
Л(
13:47
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Под Windows XP должно работать, надо выяснять в чем проблема.

В своё время (в интересах Miranda NG) были сделаны определенные усилия для работы под Windows XP и Wine.
Насколько помню, после этого поддержка Windows XP не выпиливалась.

Рекомендую подключить логирование (см. https://libmdbx.dqdkfa.ru/group__c__debug.html) и/или пройтись отладчиком.
Предположительно проблема в toolchain/компиляторе. Например, либо плохая/кривая поддержка C11_Atomics, либо TLS-конструкторов/деструкторов (Thread Local Storage).

Но выяснять в чем именно дело вам придется самим, у меня нет возможности тратить на это время.
13:51
In reply to this message
В целом, поддержка Windows была удалена из libmdbx в 2015 году, но позже "временно" возвращена в интересах Positive Technologies.
Под "временно" подразумевалось что поддержка Windows будет удалена, как только "Позитив" прекратит выпуск и поддержку продуктов под Windows, что было запланировано в 22-ом году.
Однако, libmdbx была задействована в устанавливаемые на Windows компоненты/агенты, поэтому поддержку "видны" мне приходится сохранять, хотя никаких новых продуктов под Windows в стране никто уже делать не будет.

Поддержка Windows проверяется посредством CI, то только на поддерживаемых версиях Windows (т.е. 10 и больше), при этом есть ряд известных проблем вне libmdbx:
- на платформах ARM и ARM64 компилятор MSVC (всех актуальных версий) падает по ICE (Internal Compiler Error);
- при сборке посредством MINGW периодически то возникают, то пропадают проблемы из-за ошибок toolchain, и/или сопутствующих dll (crt, exception handling, etc).
13:52
In reply to this message
Короче, по возможности я буду помогать советами, а если выясните в чем проблема, то поправлю.
Но устанавливать виртуалку с XP и отлаживать нет ни возможности/времени, ни желания.
Как-то так.
b
13:53
basiliscos
я получаю ошибку unsuitable page size 0
13:53
при этом я ещё чутка пропатчил:

-- a/src/osal.c
+++ b/src/osal.c
@@ -1090,7 +1090,7 @@ MDBX_INTERNAL_FUNC void osal_ioring_reset(osal_ioring_t *ior) {
for (ior_item_t *item = ior->pool; item <= ior->last;) {
if (!HasOverlappedIoCompleted(&item->ov)) {
assert(ior->overlapped_fd);
- CancelIoEx(ior->overlapped_fd, &item->ov);
+ CancelIo(ior->overlapped_fd);
}
if (item->ov.hEvent && item->ov.hEvent != ior)
ior_put_event(ior, item->ov.hEvent);


т.к. данной ф-ции нету под winxp, а у меня 100% из 1 треда зовётся
13:54
mingw-gcc-11
Л(
13:54
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Значит не отработал глобальный конструктор, т.н. код инициализации вызываемый при загрузке DLL или при вызове TLS-конструкторов.
b
13:54
basiliscos
In reply to this message
если ручками дёрнуть global_ctor() то сегфолт
13:56
In reply to this message
хотелось бы ручку "ручной инициализации" с пометкой "you know what u r doing" :)
Л(
13:56
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Перед ним нужно вызывать mdbx_winnt_import(), см. код внутри DllMain().
Рекомендую собрать библиотеку в виде dll, избавитесь от массы проблем.
b
13:57
basiliscos
в общем-то на текущем виде пока всё ).
Л(
13:57
Леонид Юрьев (Leonid Yuriev)
In reply to this message
см MDBX_MANUAL_MODULE_HANDLER
b
13:57
basiliscos
In reply to this message
хорошо, спасибо, попробую )
Л(
14:00
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Лучше один раз полностью прочитайте mdbx.h, в частности блок комментария со строки 695 в текущей master-ветки.
Короче, как всегда RTFM ;)
🔥
b
b
15:58
basiliscos
да, подтверждаю, как dll работает норм, а вот статически слинкованное - нет (крэшится, mdbx_module_handler({}, {}, {}); вызывается). В принципе, мне не так важно. Ещё раз спасибо за фидбэк и наводки! 👍👍👍
b
18:55
basiliscos
In reply to this message
а с этим патчем можно что-нибудь сделать?

Если без него, то вот так:

/home/b/development/cpp/syncspirit/lib/mbdx/src/osal.c: In function 'osal_ioring_reset':
/home/b/development/cpp/syncspirit/lib/mbdx/src/osal.c:1093:9: warning: implicit declaration of function 'CancelIoEx'; did you mean 'CancelIo'? [-Wimplicit-function-declaration]
1093 | CancelIoEx(ior->overlapped_fd, &item->ov);
| ^~~~~~~~~~
| CancelIo
[ 3%] Linking C shared library libmdbx.dll
/home/b/development/cpp/mxe/usr/bin/i686-w64-mingw32.static-ld: CMakeFiles/mdbx.dir/objects.a(alloy.c.obj):alloy.c:(.text+0xb23): undefined reference to `CancelIoEx'
Л(
22:35
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Буквально с этим патчем никак, ибо поломает работу во всех случаях кроме вашего "строго однопоточного".

Использование CancelIoEx() действительно ломает совместимость c Windows XP, повышая минимальные требования до Windows Vista.
Как-либо исправлять/улучшать эту ситуацию не планируется.

Могу посоветовать только два варианта:
- поднять минимальные требования до Vista, либо до "семерки";
- применять собственный локальный патч.
22:43
In reply to this message
Подозреваю что крешится из-за неверного использования, т.е. вызова с неверными параметрами.
Семантика вызова у этой функции такая-же как у DllMain(), см. https://learn.microsoft.com/en-us/windows/win32/dlls/dllmain

Соответственно, при запуске нужно вызывать mdbx_module_handler((HMODULE)0, DLL_PROCESS_ATTACH, 0).
b
22:45
basiliscos
In reply to this message
а опций "строго однопоточного" использования libmbdx не планируется? я и nix- использую его в таком режиме. Тогда не нужны atomics/critical-sections/thread-local's и т.п. Заодно этот кейс бы покрыло )
Л(
22:59
Леонид Юрьев (Leonid Yuriev)
In reply to this message
libmdbx ориентирована на работу/взаимодействие роя процессов, поэтому атомики и т.п. нужны даже если все эти процессы будут однопоточные.

Если же реализовать поддержку однопоточно+однопроцесного режима, то конечно можно немного экономить на примитивах синхронизации и атомиках.
Однако, такие сценарии использования исчезающе редки.
Поэтому не представляется рациональным усложнять исходный код и увеличивать объем тестирования.
25 April 2024
DH
11:58
Dvir H
what are the cons of MDBX_LIFORECLAIM? From what I read in the chat and the documentation it looks like the only disadvantage is to prevent auto-compafication.
If I am writing only values that fit into a page and the total size of the database only increases, should I expect the disadvantages of LIFO in comparison to the default FIFO?
Л(
12:25
Леонид Юрьев (Leonid Yuriev)
In reply to this message
MDBX_LIFORECLAIM flag turns on LIFO policy for recycling a Garbage Collection items, instead of FIFO by default. On systems with a disk write-back cache, this can significantly increase write performance, up to several times in a best case scenario.

LIFO recycling policy means that for reuse pages will be taken which became unused the lastest (i.e. just now or most recently). Therefore the loop of database pages circulation becomes as short as possible. In other words, the number of pages, that are overwritten in memory and on disk during a series of write transactions, will be as small as possible. Thus creates ideal conditions for the efficient operation of the disk write-back cache.


RTFM https://libmdbx.dqdkfa.ru/group__c__opening.html#gga9138119a904355d245777c4119534061a37585486d4e99bcb599cc15b408137b0
c
12:26
chen
does libmdbx support delete range api?
Л(
12:43
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Currently NO.

In most use cases, this feature is easily implemented through deletion in a loop, where an overhead costs are relatively low and acceptable.
If you really need to significantly reduce these overhead costs (mainly by deleting entire DB-pages), then submit a feature-request on Gitflic.
DH
12:52
Dvir H
In reply to this message
I read this and the cons of this configuration value are not described. In the chat, I read it can prevent auto-compafication by taking free pages first from the end of the database. This is the only disadvantage I found.
Specifically, my question is
Does there are other disadvantages of this option that are not described (compared to the default)?
Л(
13:50
Леонид Юрьев (Leonid Yuriev)
In reply to this message
No
👍
DH
Chris Li invited Chris Li
4 May 2024
walter invited walter
5 May 2024
Deleted invited Deleted Account
6 May 2024
w
12:52
walter
>>> Читающие транзакции при использовании MDBX_NOSTICKYTHREADS перестают использовать TLS (Thread Local Storage), а слоты блокировок MVCC-снимков в таблице читателей привязываются только к транзакциям. Завершение каких-либо потоков не приводит к снятию блокировок MVCC-снимков до явного завершения транзакций, либо до завершения соответствующего процесса в целом.

I want to confirm use MDBX_NOSTICKYTHREADS with thread. if the thread crashed then the txn will not released.
12:53
Buf if the process exit, then the txn will be auto released (read and write)? (in case crashed)
13:00
And please share the roadmap or 1.31 release date.

MDBX_NOSTICKYTHREADS is very useful for go and rust.
Л(
13:18
Леонид Юрьев (Leonid Yuriev)
In reply to this message
In libmdbx a "lock" for reading is fundamentally different from a lock for writing.

1. All inter-process locks (a read-locks and the write-lock) will be recovered/released on a process-owner crash.
This is true for all platforms/OS and for all synchronization/locking methods (see the MDBX_LOCKING build option).

2. After a thread crash inside a process you should assuming that such process is in unrecoverable state, i.e. it should be aborted/terminated.
Actually, in some operating modes, when selecting some locking mechanisms, on some operating systems, such recovery after a failure of one of the threads can be performed.
However, this is a fragile, unreliable path and generally does not work due to operating system limitations, etc.
👍
w
w
13:26
walter
Thanks for the nice explain. I plan to restart the process if any thread crashed. (I think in this case the mdbx will work agian if the bugs cause crash is fixed in a new app binary)
Л(
13:29
Леонид Юрьев (Leonid Yuriev)
In reply to this message
The short answer is at the end of May.

There are some update_gc() improvements in the current code (the master branch) that lead to regression in certain rare case.
I have not been able to fix the problem in a conservative way (without significantly changing the code) in the last 2 weeks.
So I started a more substantial reworking of the code.
If by the end of May there is a result that looks good and will not cause any slightest suspicion of reliability, then all the improvements will be included in the release.
Otherwise, I will revert some of the changes and (also) made a release at the end of May.
👍
A
w
w
13:33
walter
Thanks for the great work. I will try start use 1.3 at end of may and report the feedback
8 May 2024
Nil Medvedev invited Nil Medvedev
10 May 2024
w
20:04
walter
in a linux container , mdbx_txn_begin_ex blocked and never return for non-root user.

I use readonly mode with flags = 131072. it work for root user.

what can cause this ?

the mdbx file owner is the same non-root user
Л(
20:08
Леонид Юрьев (Leonid Yuriev)
In reply to this message
See the "Containers" section inside the README.md
The use strace tool for digging if doubt.
w
20:10
walter
I can not open you link . will try setup VPN


the last line strace

futex(0x7f14a6d45180, FUTEX_WAIT_PRIVATE, 2, NULL
20:20
In reply to this message
in the container or the host, there is only one app open the database (in side the container app open it).
20:29
There is a write run inside systemd ( same continer but inside systemd)
20:30
after I reboot. it not block any more.

I guess systemd is not in same namespace as normal process ?
Л(
20:37
Леонид Юрьев (Leonid Yuriev)
In reply to this message
I am sure there are no any problems in case an application(s) with MDBX-database running inside a single container.

However, there can be a lot of troubles when using the same DB simultaneously from different containers and/or from the container and the host.
In general this could work for reading (read transactions), but not for writing (write transactions).

In your case the syscall is blocked/waiting for futex/mutex, i.e. until a shared posix mutex will be released by other process.
This wait may never ends if the current owner of the futex/mutex is in another container or namespace.
👍
w
12 May 2024
Deleted invited Deleted Account
15 May 2024
n
12:45
nicm
In LMDB/MDBX, is iterating through a database, for reading, with a cursor just as fast (memory mapping etc.) with a read-write txn as with read-only txns?
12:46
Also, what happens when during a cursor iteration with a read-write txn, if I also (somewhere in that for loop) mdb_put/mdbx_put some data into the same database? I wonder esp. about the effects on the cursor (does the cursor automatically "shift" or could it get corrupted?) and effects on iteration performance.
AS
16:38
Alex Sharov
In reply to this message
yes. yes. yes.
rwtx has also "dirty space".
👌
n
Л(
16:43
Леонид Юрьев (Leonid Yuriev)
In reply to this message
The write transaction may require additional costs depending on the operating mode.
Briefly, there are no additional overhead in the MDBX_WRITEMAP mode.
Otherwise, during the search, reading each database page requires first searching for it in the list of shadowed/dirty pages (see Shadow paging).

Such a "dirty page list" is automatically lazily sorted in MDBX for subsequent binary search.
So a noted overhead may include sort of the DPL (by tweaked non-recursive quicksort-based or radixsort) and then fast binary branchless search.

Both implementations of sorting and searching seems to be the fastest of all known ;)
👍
n
16:46
In reply to this message
MDBX implements a "cursor tracking", i.e. an "automatic shift" ones.
So after any operations, all cursors will be in an expected/adequate state.
👍
n
Vyacheslav Lukianov invited Vyacheslav Lukianov
18 May 2024
Л(
16:42
Леонид Юрьев (Leonid Yuriev)
СМЕНА ЛИЦЕНЗИИ (THE LICENSE CHANGE)

OpenLDAP Public License → Apache 2.0

Первоисточник текста формулирован на Русском языке, который является родным для автора.
Предполагается что все заинтересованные могут легко воспользоваться машинным переводом, который при всех недостатках сможет донести суть, намерения и местам даже передать тональность.

The original source of this text is in Russian, which is the author's native language.
It is assumed that all concerned can easily use machine translation, which, with all the disadvantages, will be able to convey the essence, intentions and, in some places, even convey the tonality of a wording.

1. Причины

1.1. Лицензия Apache-2.0 является одной из самых популярных, так как содержит ряд уточнений, проясняющих и упрощающих использование исходного кода в производных работах и больших проектах.
Эти особенности лицензии Apache-2.0 я нахожу достаточно ценными и удобными. Соответственно, переход на лицензию Apache-2.0 полезным в целом.
1.2. Проект OpenLDAP имеет определенную известность, в том числе, к сожалению, среди специалистов славится кране плохим качеством кода и сбоями при отходе от простых/базовых сценариев использования.
Поэтому использование лицензии OpenLDAP, в глазах части аудитории, бросает тень на качества кода libmdbx, несмотря на то, что исходный код библиотеки переписан, в том числе, с целью повышения качества, надежности, стабильности и пригодности к тестированию.

Отмечу, что здесь не место для обсуждения объективности подобных мнений и причин, равно как и не место для оценки компетентности специалистов высказывающих такие суждения.
Однако, здесь необходимо озвучить сам факт наличия такой негативной коннотации качества кода при упоминании OpenLDAP, совершенно без намерения как-либо задеть или обидеть контрибьюторов OpenLDAP.

1.3. С точки зрения исходного кода, к настоящему времени libmdbx стала совсем другим продуктом, о котором правильнее сказать что разработка была вдохновлена LMDB, нежели является её продолжением.
Смена лицензии на переписанный код подчеркивает что это действительно новый исходный код.

Исторически проект libmdbx отпочковался от OpenLDAP почти 10 лет назад (в начале в составе проекта ReOpenLDAP).
Всё это время библиотека развивалась своим путем и более 5 лет не импортировала каких-либо доработок из LMDB из-за отличий в исходном коде.

2. Легитимность

2.1. Исходная лицензия OpenLDAP 2.8 и актуальная лицензия Apache 2.0 совпадают по базовым условиям.
При этом лицензия Apache 2.0 уточняет, определяет и проясняет многие аспекты.
Поэтому смену лицензии я склонен трактовать как уточнение, но как принципиальное изменение, которое могло-бы нарушить чьи либо права.

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

Основываясь на собственной субъективной оценке кодовой базы, включая соотношения «нового», «заимствованного» и «общеупотребительного» исходного кода, я считаю что смена лицензии допустима.
Одновременно с этим, я понимаю и признаю, что можно найти повод, чтобы трактовать ситуацию как «стакан наполовину полон/пуст».
Поэтому декларирую готовность принимать претензии и устранять их путем полного переписывания исходного кода, который попадает под критерии «заимствованного» и кто-то из контрибьюторов которого против изменения лицензии.
16:42
2.3. Вне зависимости от истории происхождения каждой строки исходного кода и её буквального авторства, прошу не считать производимую смену лицензии, и связанных с этим технических действий, как попытку плагиата, присвоения чужого труда, присвоения авторства или принижения вклада других авторов/контрибьторов.
Безусловно проект MDBX/libmdbx не появился бы без LMDB и всех участников проекта LMDB, в особенности Говарда Чу (Howard Chu), Холлварда Фурусет (Hallvard Furuseth) и Мартина Хеденфок (Martin Hedenfalk).
Как-бы исходный код не переписывался он всё равно будет основываться на базовых идеях и включать основные концепции LMDB.

3. Последствия и актуальные требования

Всё очень просто.
Потребуется обеспечить требования новой лицензии в соответствии с 4-м пунктом (см https://www.apache.org/licenses/LICENSE-2.0).
В частности, при использовании/распространении libmdbx потребуется обеспечить наличие файлов с текстом лицензии и файла NOTICE, а также обеспечить пользователям возможность ознакомиться с их содержимым в работах/продуктах использующих libmdbx.
👍
VS
AV
A
16:43
На днях соответствующий коммит появиться в git-repo.
?
19:05
𝓜𝓲𝓬𝓱𝓪𝓮𝓵
Здравствуйте! Я не знаю, не является ли уже это запретной темой, но каков статус и примерные сроки релиза MithrilDB? Имеет смысл подождать для личного проекта или не стоит?
Л(
19:40
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Пока мне нечего добавить к https://t.me/libmdbx/4160.
В целом я стараюсь не давать обещаний.
👍
?
?
22 May 2024
n
07:17
nicm
What are good ways to read-only iterate through an entire large database (e.g. to collect some custom statistics)?

Just one read-only txn for the whole db is a bad idea because it's long-lived, right?

Here is another approach: Do it in smaller batches (one read-only txn per batch). At the end of each batch, remember the last key processed. At the start of the next batch, use MDB_NEXT to find the new starting point. And so on.

☝️Is this a good way? Are there any better ways? Thanks.
AS
12:16
Alex Sharov
In reply to this message
- MDBX_NEXT is just one of constants - cursor has many of them - to find key, to find key by prefix, etc... See mdbx.h

- Can run many parallel read transactions - if need parallel reads from disk.

- Also table can be DupSort (unlimited amount of values in one key).

- some statistic can be obtained by _info or _stat methods.
👍
n
29 May 2024
Л(
16:03
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Although belatedly, it is worth adding to what has been written:

1. Anyway you need a long-lived transaction to scan a whole DB.
So, there may indeed be effects described in the "Restrictions & Caveats" section.
To avoid ones, you can:
- start a write-txn instead of read-txn;
- time-to-time call mdbx_txn_straggler() or mdbx_txn_info() to check to assess the situation and interrupt/restart the read transaction or scan as a whole.
- use MDBX_hsr_func on the writer side and kick/kill sanner-reader by a signal.

2. Running multiple read transactions to scan a large memoy-mapped database may be useless, or even harmful.
If the database does not fit into RAM, then the OS will have to swap-in its pages from disk.
In short, most all of OSes unable perform such paging in parallel/multithreaded, especially for a single process.
Thus, another bottleneck may be if several transactions compete to swap pages from the disk.
However, multiple parallel transactions will be a good solution if significant processing is expected for the data, which will take at least as long as reading from disk.

3. With a large number of records, it may be useful to think about reducing the overhead of data iterating.
So the mdbx_cursor_scan() could be a best choice.
👍
L
n
Л(
16:39
Леонид Юрьев (Leonid Yuriev)
Изменение API / The API change:

Support of mdbx_cursor_get_batch(..., MDBX_GET_CURRENT) will be dropped in v0.13.x, but MDBX_FIRST and MDBX_NEXT will still work with batch-get.

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

Честно говоря, необходимость mdbx_cursor_get_batch(..., MDBX_GET_CURRENT) у меня всегда вызывало большие сомнения, но такое поведение было реализовано для совместимости (см. https://libmdbx.dqdkfa.ru/dead-github/issues/236).
👍
w
AS
AS
16:48
Alex Sharov
In reply to this message
Cloud drives (pd-balanced, gp3) are parallel-throughput-oriented high-latency. So, parallel disk read is very helpful.
Л(
19:14
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Глянул актуальный код filemap_fault() в mm/filemap.c
Всё стало сильно лучше, если смотреть на LTS, то с 5.15.
Эксклюзивных блокировок почти нет и на-вскидку ничего не мешает параллельным page-in в разных потоках.
5 June 2024
Сергей С invited Сергей С
6 June 2024
makcandrov invited makcandrov
chen invited Arthur (George)
A
10:39
Arthur (George)
Hello Everyone,
I have some general questions about libmdbx, appreciate for any help.
1. Is there cache layers for libmdbx? When I am researching lmdb, it claims that there is no cache layer anymore compared to BDB.
2. I am wondering how the mmap() works - for example, when there are 1T db storage and 64G RAM,
how the page/mmap() works when there are reads/writes happening? Any materials/documents for this.
3. Is lmdb/libmdbx uses more memory (RAM) for mmap(), especially when there are lots of reads/writes?

Thanks agin.
AV
11:39
Artem Vorotnikov
In reply to this message
1. For performance purposes, libmdbx relies entirely on OS caches. There is mechanism for stacking modified database pages in memory for non-writemap mode, but this is purely to enable nested transactions.
2. mdbx attempts to map database into available memory opportunistically. That is, the more RAM is available, the more DB pages are 'hot'. If entire DB fits into RAM, then it's obviously the best case for performance.

Leonid can correct me if some of these answers are wrong.
👍
A
A
11:45
Arthur (George)
In reply to this message
For “mdbx attempts to map database into available memory opportunistically”, if the DB on disk is much bigger than RAM, how mdbx chooses which pages to keep in RAM, is it similar to LRU? How to decide which pages are hot? For our case, the RAM is 128G and DB is about 1T, ususally the hot data is not as big as 128G, however we can see the RAM is almost full - it is also maybe caused by our program, I am not sure about this.
AS
11:51
Alex Sharov
In reply to this message
OS does this, can google "how mmap works in linux" and "madvise syscall"
A
m
12:10
makcandrov
Hello, I need to read from the database consistently and from multiple threads. So, I need to create multiple read-only transactions, each pointing to the same database snapshot (with the same transaction/snapshot ID). I could create them all at once, but I can't know in advance how many I'll need. Because of this, I need to create a transaction that points to the same database snapshot as another transaction that was created earlier (and isn't closed yet), even if some writes have happened in the meantime.

From my (limited) understanding, the snapshot/transaction ID is the field txn.mt_txnid, which is always set to head.txnid for a read-only transaction. Nested transactions can have the same txnid as their parent, but they aren't available for RO transactions.

Any help with this would be appreciated. Thanks!
Илья Миχеев 🍃 invited Илья Миχеев 🍃
Л(
14:00
Леонид Юрьев (Leonid Yuriev)
In reply to this message
A few clarifications to the answers already given.

1.1.
Yes, libmdbx relies on OS cache. Actually it is an “unified page cache” in most of actual OSes.
This is a conscious architectural decision that has both pros and cons.
Pros: direct data access (no overhead), transparency of cache behavior, better RAM apportion between applications by OS kernel.
Cons: reading data via a page-fault exception/interrupt, synchronous reading only (no ability to asynchronous), unnecessary page-fault+page-in on page-allocation (now MDBX avoids this by “prefault-write”).

From the implementation point of view, there are two reasonable ways:
- implementing a self-own page cache with asynchronous I/O.
- just rely on mmap() and OS unified page cache.

Historically LMDB and MDBX uses secondary approach, which is very good for cases when entire DB or a hot part of data fits into available RAM.

Nonetheless, LMDB has an edition (a separate branch in git) with a simple caching mechanism for the possibility of using large databases (> 4GB) on 32-bit systems. This seems to me to be a rather clumsy solution, just a crutch to be able to somehow work with large databases on 32-bit platforms.

1.2.
For non-writemap mode (without MDBX_WRITEMAP) libmdbx internally uses modified (aka dirty) page list for https://en.wikipedia.org/wiki/Shadow_paging. Briefly it is a list of pages with should be written to a disk on transaction commit.
Such dirty page list (aka DPL) works as a cache in most cases, including LRU and spill-out for large transactions.
Also DPL given ability for a nested transactions with some cost of overhead.

In the writemap mode (with MDBX_WRITEMAP) libmdbx prefers to minimize overhead, don’t use DPL and thus disables nested transactions.

3.
A large write transactions (i.e with a lot of updates/insertions) in non-writemap mode (without MDBX_WRITEMAP) uses a large memory for shadow paging (see above).
A
14:04
Arthur (George)
In reply to this message
Thanks a lot. It is very useful and helpful. 👍
AS
16:55
Alex Sharov
In reply to this message
As i know LMDB has git branch with page-encryption feature. Maybe cache useful for this also, i don’t know.
Л(
17:36
Леонид Юрьев (Leonid Yuriev)
In reply to this message
For now libmdbx does not provide an API for cloning a read transaction.
There were two reasons for this:

1. In most operating systems, there was (actually is) a rather bad situation with the implementation of paging pages from different threads.
So, approaches with massively parallel multithreaded reading almost immediately get stuck in this bottleneck.
Nowadays only modern Linux seems to be clean from exclusive blocking in a page-in path and thus able to effective handle multithreaded data swap-in for memory-mapped DB.

2. Without MDBX_NOSTICKYTHREADS, a read transactions must be binded/pinned to threads, and this requires using thread API in a non-portable way. Therefore cloning read transactions could be performed from a "target" threads which will be executed such transactions.
For now I have not yet found a sufficiently convenient and flexible form/design of API suitable for use in different languages/environments that would be worth implementing.

- - -

I will try to add something similar to int mdbx_txn_clone(const MDBX_txn *origin, MDBX_txn **clone) to the API.
However, I'm very busy right now and I'm not ready to promise anything.

I think now you should just create several transactions from different threads, then compare their transaction IDs and restart lagging transactions (which have a lower txnid than others).
Л(
18:00
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Yes, but page-encryption feature is irrational for memory-mapped DB.
Actually a cache of decrypted pages is needed, but mmap() becomes completely useless in this case and just increase the size of page tables.
m
19:17
makcandrov
In reply to this message
Ok, this is mostly what I have done so far. Thank you so much for your answer!
13 June 2024
G
18:34
Giulio
Hello, I am trying to tune MDBX to have good commit times/good write times, etc... Currently I have got a small DB with 5 GB of data with WRITE_MAP feauture enabled. the entire DB is loaded in memory:


admin@erigon5900d:~/erigon$ vmtouch -v chaindata
vmtouch: WARNING: unable to stat chaindata (No such file or directory)

Files: 0
Directories: 0
Resident Pages: 0/0 0/0
Elapsed: 6.5e-05 seconds
admin@erigon5900d:~/erigon$ vmtouch -v gg/chaindata
gg/chaindata/mdbx.lck
[OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO] 251/251
gg/chaindata/mdbx.dat
[OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOo] 1532416/1536000

Files: 2
Directories: 1
Resident Pages: 1532667/1536251 5G/5G 99.8%
Elapsed: 0.048304 seconds
18:35
However commit times are really weird, basically 20-30ms for data being written in order of 1-2 megabytes and the entire time is spent on WRITE_MAP. Why could this be the case? shouldn't writting to memory be a quick operation?
18:37
Said that, there is a decent amount of random writes, but I suspect that it is still an exaggerated time just to update some memory, also considering that the entire thing is in Page Cache
Л(
18:42
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Basically, a 'transaction commit" means all data written/synced to a SSD/HDD.
Otherwise, data will be lost on system failure (i.e. power failure, kernel OOPS, etc).
G
18:42
Giulio
In reply to this message
i know that, this runs on an NVME
18:45
but again, this is on a very small DB with small data and the WRITE_MAP takes 100% of the times, the sync time does not even take an ms
18:45
I was just wondering if there is some parameter I can look into tuning in to make the times a little bit better
Л(
18:47
Леонид Юрьев (Leonid Yuriev)
In reply to this message
The same.

For data durability libmdbx must call msync() (with MDBX_WRITEMAP) either fdatasync() (without MDBX_WRITE MAP), and then kernel will check each PTE, write dirty one to the media and issue some sync/flush command to a NVMe
G
18:48
Giulio
but sync time is 0
18:49
are you saying that it is just the time needed to sync the map with the disk? then why is the metrics of WRITE_MAP and FSYNC different?
Л(
18:52
Леонид Юрьев (Leonid Yuriev)
In reply to this message
libmdbx has the feature to collect latencies (time costs) during committing a transaction.
Please ask Erigon team how to get this info.
G
18:53
Giulio
yes we do
18:53
we collect this info using MDBX latency feauture and the result is the WRITE_MAP is what takes the most time
18:54
I am part of Erigon, anyway is there any way to tune it?
Л(
18:54
Леонид Юрьев (Leonid Yuriev)
In reply to this message
The "WRITE_MAP" is just a mode of operation, but not an operation, i.e. it cannot take a time.
18:55
In reply to this message
G
18:56
Giulio
it is write
18:56
i was told by alex this is the result of WRITE_MAP, maybe I misunderstood. apologies
18:59
* i.e. the summary duration of a `write()` syscalls during commit. */
uint32_t write;
AS
18:59
Alex Sharov
In reply to this message
Print all latencies which returns tx.Commit
Л(
19:06
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Basically, with the MDBX_WRITEMAP the write-latency metric should be 0, since no write() syscall(s) should be, but only msync().

Otherwise the MDBX_AVOID_MSYNC != 0 (it is a built-time option, especially for Windows where msync is madly slow).
G
19:09
Giulio
COMMIT LATENCY                                                                                                                                                                                                        
WRITE_LATENCY (ms) 15
SYNC_LATENCY (ms) 0
ENDING_LATENCY (ms) 0
WHOLE_LATENCY (ms) 15
19:09
yeah i know but it is not, makes no sense to me, unless we are actually messing the latency assignment @AskAlexSharov
Л(
19:12
Леонид Юрьев (Leonid Yuriev)
In reply to this message
This is useless for me.
Please show at least the 8 integer fields of struct MDBX_commit_latency.
AS
19:12
Alex Sharov
In reply to this message
Where did you get it?
G
19:13
Giulio
In reply to this message
I just printed the latency we got
19:14
In reply to this message
Ok so the "WRITE" is the "write" field in that struct fyi, but I can format it a little bit better if that is what you want:



struct MDBX_commit_latency {
/** \brief Duration of preparation (commit child transactions, update
* sub-databases records and cursors destroying). */
uint32_t preparation;
/** \brief Duration of GC update by wall clock. */
uint32_t gc_wallclock;
/** \brief Duration of internal audit if enabled. */
uint32_t audit;
/** \brief Duration of writing dirty/modified data pages to a filesystem,
* i.e. the summary duration of a `write()` syscalls during commit. */
uint32_t write;
/** \brief Duration of syncing written data to the disk/storage, i.e.
* the duration of a `fdatasync()` or a `msync()` syscall during commit. */
uint32_t sync;
/** \brief Duration of transaction ending (releasing resources). */
uint32_t ending;
/** \brief The total duration of a commit. */
uint32_t whole;
/** \brief User-mode CPU time spent on GC update. */
uint32_t gc_cputime;
....

"WHOLE" is whole, we dont collect all of them
19:16
In reply to this message
for alex's interest


fmt.Println("COMMIT LATENCY")
fmt.Println("WRITE_LATENCY (ms)", latency.Write.Milliseconds())
fmt.Println("SYNC_LATENCY (ms)", latency.Sync.Milliseconds())
fmt.Println("ENDING_LATENCY (ms)", latency.Ending.Milliseconds())
fmt.Println("WHOLE_LATENCY (ms)", latency.Whole.Milliseconds())
AS
19:19
Alex Sharov
In reply to this message
Try go inside mdbx-go bindings and print C struct for Leonid. He doesn’t know about our mappings/parsings.
G
19:22
Giulio
I see it is assigned by _stat.write so i am quite sure it is that field
19:22
But I will do it later today
AS
19:23
Alex Sharov
In reply to this message
Also you can print tx.Info() before commit - to see how much dirtySpace (dirty pages there).
Л(
19:27
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Ok.

1. If we see the "write" > 0, then a write() syscalls were, which should not been in "write map" mode.

2. Even in non-writemap mode, for writing a more than few pages, libmdbx use write() with non-sync/lazy fd-descriptor and subsequent fdatasync().
Therefore write_latency == 15ms with sync_latency == 0 seems are impossible/invalid/wrong metrics.
G
19:27
Giulio
Ok will do as alex said
14 June 2024
G
21:16
Giulio
as it turns out, there was just miscommunication beetwen me and alex, actually, we did not have WRITE_MAP enabled
21:17
I know what to do, apologies for the confusion
Л(
21:18
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Nonetheless, pay attention to the second point of my answer above.
n
21:49
nicm
Can abort txn be running for a long time (e.g. if there were many previous puts in a long-lived read-write transaction)? Or is there some "magic" that always make aborts fast no matter what?
Л(
22:20
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Basically, no — the "magic" is enough to abort transaction quickly, but it depends on many factors:
- for nested transactions: assertion-enabled builds will check dirty page list, which could lead to load pages from disk (page-in);
- for nested transactions: assertion-enabled builds will perform audit of pages usage, which required to read all GC records;
- without MDBX_WRITEMAP: shadowed/dirty pages will be released, which may take some time for large transactions;
- etc.
👍
n
15 June 2024
Л(
23:29
Леонид Юрьев (Leonid Yuriev)
// non-Russian-speaking participants/users — please use machine translation.

В ветке devel уже доступна версия libmdbx, которая с минимальными ad hoc доработками станет очередным мажорным выпуском v0.13.1
Несколько слов и просьб по этому поводу:

1. Смена лицензии.
Как было озвучено ещё в апреле, произведена смена лицензии libmdbx на Apache 2.0
Буду очень признательным если вы оцените формулировки в файле COPYRIGHT (пока только в ветке devel), где изложены причины/мотивация, а также соображения по поводу легитимности.

2. Переработка курсоров.
Исторически в реализации курсоров были две проблемы:
- непрозрачный нерегулярный код, в том числе, с особенностями в виде лишних/дублирующий полей (с точки зрения алгебры отношений/состояний);
- эффект «неопределенного поведения» (aka UB), с точки зрения пользователя, в ряде ситуаций.

Поэтому, ожидаемо, я предпринял усилия, чтобы выбросить проблемный код одновременно со сменой лицензии.
Текущая реализация является компромиссом между «хотелось» и «получилось», так как очень многое из желаемого меняет поведение и ломает совместимость с огромным объемом уже написанного и отлаженного кода.

В плохих ситуациях новый код возвращает (должен возвращать) код ошибки (преимущественно MDBX_ENODATA) и этим пресекает развитие ситуации в сторону UB.
В текущем понимании, это не создаст каких-либо проблем, кроме как в случаях неверного использования API с последующим UB.

Сценарии приводящие к UB следующие:
а) Использование курсора, с опорой на его текущую позицию, после неуспеха операций предполагающих установку курсора на конкретное значение ключа (MDBX_GET_BOTH, MDBX_GET_BOTH_RANGE, MDBX_SET, MDBX_SET_KEY).
Раньше проблема была в том, что libmdbx допускала операции (например удаление в текущей позиции) после неуспеха таких операций, но при этом реальная позиция курсора зависела как от содержимого БД, так и от предыдущей позиции курсора, а также от поведения конкретной версии кода (зависимость от микрооптимизаций и несущественных изменений код).

Технически, UB возникает как зависимость поведения/состояния курсора от скрытого от пользователя состояния/содержимого БД. Другими словами, поведение всегда детерминировано, но зависит от невидимой для пользователя информации.

б) Использование курсора после серии вставок и/или удалений через другие курсоры.

Алгоритмически/технически тут проблема в том, что предполагаемая пользователем позиция курсора может принципиально отличатся, либо вообще становится неопределенной.
Характерный пример: если мы установим курсора на некоторую позицию, потом через другой курсор удалим все записи и после добавим одну. Где в такой ситуации должен оказаться первый курсор и что удалить при вызове mdbx_cursor_del() ?

TL;DR

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

Поэтому просьба = попробовать/протестировать в самое ближайшее время.
👍
UD
N
A
4
16 June 2024
Eugene invited Eugene
Александр invited Александр
17 June 2024
Л(
13:29
Леонид Юрьев (Leonid Yuriev)
🔥
YS
G
ИМ
5
Igor Sidorov invited Igor Sidorov
18 June 2024
DH
10:11
Dvir H
What is the meaning of ms_depth in a table using the MDBX_DUPSORT flag? As far as I understand, if a key has many values, the values will be stored in a new tree, so the depth for the different values could be different.
Л(
12:35
Леонид Юрьев (Leonid Yuriev)
In reply to this message
With the MDBX_DUPSORT a tree-structure may be nested, i.e. if a key (a tree' node) have a lot of multi-values ones will be stored in a nested tree attached to a such node.

The ms_depth always means the height of main/first tree, but knows nothing of any nested.
Each nested tree of multi-value nodes have it own height depends number of multi-values.

For multi-value nodes you can get some info:
- a number of values (aka duplicates in historical terminology) by mdbx_get_ex() and mdbx_cursor_count();
- a variance of a nested tree(s) height in form of bitmask (non-zero n-bit means a presence of nested tree(s) height n) by mdbx_dbi_dupsort_depthmask().
👍
DH
n
13:38
nicm
How fast is MDB(X)_NEXT? (Just wondering if it's O(logn) based on key/value size, or something else.)
Л(
13:42
Леонид Юрьев (Leonid Yuriev)
In reply to this message
The total cost of iterating whole key-value table/db is
O(Log(N) + N).

Thus the amortized/averaged cost of single iteration is
O(Log(N)/N + 1).

So, it is very fast.
n
14:34
nicm
In reply to this message
Is N just the number of key-value pairs? O(Log(N)/N + 1) looks a bit counterintuitive (to me) because beyond a certain point it decreases (less costs for more N). I'm clearly missing something from my understanding of things.

(My original motivation for asking: For same fixed number of pairs, I'm seeing a linear relationship between key&value sizes (key size = value size, each 8 bytes up to 504 bytes) and nanoseconds spent per iteration (6.3 up to 70). Was hoping to better grasp the reasons for this.)
Л(
15:05
Леонид Юрьев (Leonid Yuriev)
In reply to this message
1.
Yes, the O(Log(N)/N + 1) is right basically.
However, you must consider the limitations of an expressiveness of O-notation.
In other words, either we have to go deeper and detail a lot, or simplify as much as possible and leave only the main thing.
The simplest answer is O(1) for single move-to-next operation.
A slightly more detailed answer is the one I gave.

2.
On a nanosecond scale, the result will also be influenced by the size of the data and the specific tree structure formed by all previous operations.
For instance:
- in case of a few short multi-values, ones will be placed on a sub-page, which will be stored itself as a complex pseudo-value of the key, i.e. not on a nested tree;
- but in case a lot of multi-values or a long ones, a nested b-tree will be constructed;
- on a next step, costs of sup-page and nested tree are different, at that in some scenarios one thing will be more profitable, and in others another.

Further, with an increase in an amount and/or volume of items, the cost of operations will be expected to increase, but there will be obvious rungs/degrees/steps on an increase the height of a tree.
etc...
👍
n
DH
15:53
Dvir H
In reply to this message
What about ms_branch_pages, ms_leaf_pages, and ms_overflow_pages? Are they also only for the main tree or for all the table?
Л(
16:03
Леонид Юрьев (Leonid Yuriev)
In reply to this message
These fields contain statistics for the entire table, including all nested trees, except ms_overflow_pages (since it not used for dupsort tables nor nested trees).

This is historically behavior (inherited from LMDB), and it makes sense — you have actual info/stat of a whole table except depth of nested b-trees.
👍
DH
19 June 2024
AS
04:22
Alex Sharov
In reply to this message
👍
i
Л(
20 June 2024
Л(
14:16
Леонид Юрьев (Leonid Yuriev)
Готовившееся с апреля обновление со сменой лицензии (а также реструктурированным исходным кодом, переделанными курсорами и массой других доработок) пролито в master.
Это кандидат для выпуска v0.13.1, который состоится до конца июня.

158 files changed, 40280 insertions(+), 33403 deletions(-)
🔥
AV
A
3
👍
A
21 June 2024
Pascal Berrang invited Pascal Berrang
PB
12:19
Pascal Berrang
Hey everyone. We've been using libmdbx in our project for a while now but are currently experiencing some performance degradation with a growing database. I tried to profile the issue a bit and found that when we add essentially random data (hashes or even unsorted integers) into the database, the commit operation scales linearly with the database size and not n*log(n) with the number of new entries.
The put operation scales nicely logarithmically.

The attached plot shows the time to put and commit 84000 entries per each round (32 byte random key, 32 byte value). For example, in round 10 the database already has a size of 840000 entries and we put another 84000 and commit.

Is this expected behaviour or is it a problem of our configuration? When having incrementing keys, we see constant commit times. Happy to provide more details! Thanks :)
Eva invited Eva
PB
12:52
Pascal Berrang
I also plotted the integer fields from the commit_latency struct. Seems like sync is the decisive factors here.
Л(
13:34
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Greetings.
Let's try to figure this out.

1.
I think you should use mdbx_txn_commit_ex() to get and put to the diagram additional information about the time spent and mdbx_env_stat_ex() to get the number of operations with pages.
I assume that in your scenario we will see a linear increase in page-write operations (the wops counter for non-MDBX_WRITEMAP mode, or the sync metric for writemap-mode).
Below is some simplified explanation of the reason.

2.1.
Think about how many database pages should be updated with N random key changes/inserts, i.e. the keys-per-page and how may pages should be touched if you put N random keys.

Imagine you have a small key-value DB with b-tree with 100 pages, with a 10000 of random uniform-distributed keys.
If you insert/upsert/update a 1000 random uniformly-distributed keys, then most likely each (i.e. up to 100) page will be updated/written.
In other words, in this case, several keys that are changed in the transaction fall on one page, so the cost of updating one key is much less.

But if the database is 10 times larger (1000 pages), then when inserting the same number of keys, it is likely that you will also need to update all the pages, which are 10 times more.
In other words, in this case, on average, one changed key fall on one page, so the cost of such an update is much higher.

2.2.
The second graph shows that gc_wall clock has the most time, while gc_cpu time is close to zero.
It means that when committing a transaction, most of the time is spent reading GC records from disk, i.e. there are a lot of these records and ones have to be read.

This requires additional investigation, but first you should do three things:
a) Use a non-obsolete version of libmdbx (for instance the latest release).

b) Make sure that the MDBX_ENABLE_BIGFOOT build option is enabled.

c) Copy the database with compactification (mdbx_copy -c) to get rid of the large GC.
PB
PB
13:38
Pascal Berrang
Great, thanks already, will do so and report back ☺️
PB
14:51
Pascal Berrang
The other graph still looks the same (we were on that version already and bigfoot was enabled, so no changes)
14:54
Here are the statistics captured on the transaction right before commit (log scale y-axis).
I made sure MDBX_BIGFOOT is enabled and we're using v0.12.10
14:55
> Think about how many database pages should be updated with N random key changes/inserts, i.e. the keys-per-page and how may pages should be touched if you put N random keys.

This was also an idea that we came up with. We just found it odd that sync is in a completely different complexity class than write.
14:57
Regarding the gc times, this might be a colour problem in the graph. I checked the data. gc_wallclock is constant 0, gc_cputime stays below 0.03 seconds
Л(
15:12
Леонид Юрьев (Leonid Yuriev)
In reply to this message
This is an important clarification that allows us not to dig in this direction.
Л(
15:27
Леонид Юрьев (Leonid Yuriev)
In reply to this message
It is more complex picture.

In most cases the write() syscall just put io-request in a queue, but sync() wait for these request(s) to complete and then issues a flush/sync command.
Moreover, in non-MDBX_WRITEMAP mode libmdbx uses pwrite(lazy-fd)+fdatasync() for write many pages, but just pwrite(sync-fd) for a few page cases.
With MDBX_WRITEMAP only the msync() will be used on linux (MDBX_AVOID_MSYNC=0).

Okay.
Please use mdbx_env_info_ex() to get a "page operation statistic" (mi_pgop_stat) and draw it.
PB
15:49
Pascal Berrang
Here are the pgop stats
15:51
and here the pgop stats without cow/wops to see the other curves
Л(
16:10
Леонид Юрьев (Leonid Yuriev)
@paberr, seems there is extra data here, and in a somewhat inconvenient way.

In general, I would like you to understand for yourself what is happening based on the information already provided, and then come up with a specific problem, for example, "here my estimate of the number of pages that need to be updated and written to disk does not match what we see according to statistics".

For convenience, I would recommend that you turn-off MDBX_WRITEMAP mode and analyze only wops (the number of write operations).
It should also be borne in mind that "page operation" counters are cumulative, i.e. they increase monotonously from transaction to transaction until the end of the database session (until it is closed by the last using process).
PB
16:20
Pascal Berrang
In reply to this message
MDBX_WRITEMAP is already turned off, but good point regarding the cumulative numbers. we'll have a look and see whether it matches our estimates
👍
Л(
PB
17:06
Pascal Berrang
So, we looked at the non-cumulative wops and have a couple of questions.

1. When is such a write operation triggered? The numbers we see here are too low to correspond to the number of entries we add to the db. We have 13 wops for the first round, the second round only has 4 and the fourth round goes up to 127.

2. Since the number of entries added to the db in each round is constant, we don't quite understand the shape of the curve we see here. But maybe the answer to Q1 will help with that.
Л(
17:32
Леонид Юрьев (Leonid Yuriev)
In reply to this message
The great case that shows a lot!

There is a certain subtlety in the fact that wops is a counter of operations, but not of written pages.
I.e. one write() syscall can write a few consecutive pages.
And when you start from an empty DB a significant part of the pages are not reused from GC, but are allocated sequentially from the end of the DB file (actually at the end of allocated space, not the end of file).
Therefore, starting from a scratch and (apparently) mostly with inserts, you see a gentle curve at the beginning and then a relatively steep growth (which also includes GC operations).
👍
PB
PB
17:50
Pascal Berrang
Great, thanks! That helps understand it a bit better 🙂
🤝
Л(
30 June 2024
В
10:16
Виктор
Небольшой вопрос на понимание: есть ли возможность после открытия базы получить список содержащихся в ней map-пов?
AS
10:53
Alex Sharov
In reply to this message
да. нужно open_dbi(0)
и курсором по нему пройтись
ключи - названия dbi
Л(
11:03
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Исторически выделенного для этого API не сформировалось, так как:
1) есть возможность сделать это побочным способом (как описал Алексей);
2) низкая потребность, ибо за редким исключением именованные subDb создаются явно и их набор известен.

Тем не менее, в ветке master есть API для проверки целостности БД.
При этом именованные subDb будут перечислены через обратный вызов subdb_filter,см MDBX_chk_callbacks.

Может быть успею добавить в ближайший релиз явное API (поверх упомянутого chk-функционала).
В
11:32
Виктор
Очень интересно. А subDb это базы внутри одного физического файла или они в разные файлы пишутся?
Л(
11:34
Леонид Юрьев (Leonid Yuriev)
In reply to this message
В исторических терминах (унаследованных от Berkeley DB и LMDB) named subDb — это именованные map-ы, т.е. наборы/отображения пар ключ-значения внутри одной БД (одного файла).
В
11:41
Виктор
Понятно. А через какую функцию можно создать subDb?
Л(
11:43
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Пожалуйста RTMF.
Лучше пару раз полностью прочитать mdbx.h
В
12:25
Виктор
Просмотрел mdbx.h++. Правильно ли понимаю что map_handle это и есть subDb? Если это так, то почему в моих тестах мне удалось создать около 40000 map_handle, когда стоит ограничение maximum sub-databases: 32765?
Л(
12:34
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Думаю что-то не так.
Покажите ваш код.
В
12:49
Виктор
К сожалению, сейчас на выезде, вечером доберусь до компьютера, сброшу
Л(
12:50
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Хорошо.
Но вы точно создавали/открывали 40000 разных subDb, а не одну и ту же?
В
13:03
Виктор
В тесте вызывалась в цикле функция mdbx::txn::create_map(...) каждый раз с разными именами. После создания записывались пары ключ /значение...
Л(
17:01
Леонид Юрьев (Leonid Yuriev)
В текущей ветке master обнаружена проблема, с сожалению пока без сценария уверенного воспроизведения.
Поэтому выпуск v0.13.1 снова будет отложен на 1-2 недели, как минимум до прояснения ситуации.
В
20:27
Виктор
Создал более 50000 мапов
Л(
23:09
Леонид Юрьев (Leonid Yuriev)
In reply to this message
У вас же там env.close_map(testHandle);
Соответственно, вы создаете много subDb, но активных dbi-хендлов мало.

Или это не совсем актуальный код?
1 July 2024
В
00:38
Виктор
Активный только один dbi-хендлер
Л(
00:40
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Ну примерно да, т.е. 1 + 1 + 1 < 32767 - 1 - 1
В
00:45
Виктор
) Значит это ограничение по открытым dbi-хендлам. Из документации не совсем понятно. Большое спасибо за разъяснение.
Л(
00:51
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Вот если "не совсем понятно", но вы предложите лучшую формулировку (PR или хотя-бы здесь).

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

Короче, приложите руку чтобы сделать лучше.
В
01:02
Виктор
Как вариант: Maximum of open sub-databases: 32765.
Л(
01:03
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Так предлагать не хорошо, не продуктивно.

+ Ведь никто не знает как сейчас, без того чтобы смотреть внуть.

++ Вы покажите как было и как предлагаете.

+++ С кратким пояснением, почему по-новому лучше, если это не очевидно и т.п
В
01:16
Виктор
Проблема возникла из прочтения ограничений на libmdbx. Предполагается использовать эту бд в модуле анализа данных где каждому объекту соответствуют некоторое количество данных (ключ-значение). Система сбора данных достаточно большая, в пределе количество объектов до 1млн, а количество информации по ним в районе 100 тыс. Обратили внимание на вашу очень быструю бд и пытались понять как лучше ее использовать. Немного не понятно было что такое вообще subDb и связанные с ними ограничения
01:17
Л(
01:20
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Спасибо, но зачем, кому и о чем вы пишите?

Попытайтесь в 42 знака, ну или в 84
В
01:23
Виктор
Попытался описать кейс использования. Постараюсь писать короче.
Л(
01:24
Леонид Юрьев (Leonid Yuriev)
Навеяло: "... Французские женщины познали как велик и многозначителен русский язык ..." ;)
AV
01:25
Artem Vorotnikov
Если уж начали разговор

М.б. имеет смысл уже окончательно перейти от subdb к таблицам в терминологии?
01:26
А environment - просто назвать БД
01:26
Так уже сделано в растовых привязках (Table и Database)
В
01:39
Виктор
Непростой вопрос. Проблема в том что в разных местах одно и то же понятие описано разными словами что приводит к путанице. Например SubDb, map_handle, table
Л(
01:51
Леонид Юрьев (Leonid Yuriev)
Хм, странное котэ-видео получилось, но сам коте не попал ;)
02:05
In reply to this message
Исторически некая терминология была в Berkeley DB и оттуда пришла в LMDB, а потом была унаследована сюда.

Сначала повода что-либо менять точно не было, ибо кроме смены заголовков/captions ничего не менялось и не было смысла "называть старое новым".

Теперь вроде-бы наоборот, но кто пользуется привык и новое станет раздражающим никчемным дребезгом.

Короче, не бойтесь поприседать чтобы показать как лучше.
02:05
In reply to this message
Давай коммит.
🫡
AV
2 July 2024
b
00:19
basiliscos
Здравствуйте. А как-нибудь можно получить текущий размер БД, в байтах?
AS
07:13
Alex Sharov
In reply to this message
Да. Что-то вроде env_info/env_stat
👍
Л(
4 July 2024
В
13:44
Виктор
Здравствуйте. map_handle после вызова функции commit() разрушается. Можно ли проверить каким-нибудь образом его состояние ?
Л(
15:30
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Нет, не разрушается.
Но исторически время жизни несколько непрозрачно, см. примечание к mdbx_dbi_open().

Состояние хендла можно получить посредством mdbx_dbi_flags_ex().
В целом, пожалуйста RTFM.
🙏
В
5 July 2024
СО
09:46
Станислав Очеретный
Добрый день. БД открыта с флагом MDBX_INTEGERKEY и в неё добавляем значения с signed int64 ключём.

Из документации понял, что так делать нельзя (Numeric keys in native byte order either uint32_t or uint64_t (must be one of uint32_t or uint64_t, other integer types, for example, signed integer or uint16_t will not work). The keys must all be of the same size and must be aligned while passing as arguments.)

Потестировал свою базу на работу с signed значениям, ошибок не обнаружил.
Правильно ли я понимаю, что такая база может некорректно работать при открытии на разных ОС?
Что может не работать в этом случае?
Л(
10:06
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Формат файла БД зависит от нативного порядка байт. Поэтому БД созданная на машине с little-endian не возможно открыть на машине с big-endian.
см. https://ru.wikipedia.org/wiki/Порядок_байтов

При использовании MDBX_INTEGERKEY и 64-битный ключей, эти ключи будут интерпретироваться встроенной функцией сравнения как без-знаковые значения, т.е. помещенные в БД пары ключ-значение будут отсортированы по ключам как значениям типа uint64_t.
Тут нет каких-либо проблем, кроме порядка не соответствующего ключам как значениями типа int64_t.

Если вам неважен порядок сортировки в БД, но можно оставить как есть.
Иначе, если порядок ключей важен, то есть два варианта:

1. Трансформировать ключи при помещении в БД посредством mdbx_key_from_int64(), а при чтении в обратную сторону посредством mdbx_int64_from_key().

2. Использовать пользовательскую функцию-компаратор, см.mdbx_dbi_open_ex().
Но тут проблема в том, что потом нет возможности проверить порядок ключей в такой БД, так как они упорядочены вашей функцией.
СО
10:07
Станислав Очеретный
ок
8 July 2024
b
22:27
basiliscos
In reply to this message
в каких единицах? в погуаях? Это количество байт или килобайт?
22:28
кажется байт, но лучше уточнить
Л(
22:29
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Байт
👌
b
Kirill invited Kirill
11 July 2024
Sayan J. Das invited Sayan J. Das
12 July 2024
Л(
01:28
Леонид Юрьев (Leonid Yuriev)
In reply to this message
В API добавлена функция mdbx_enumerate_subdb().
👍
В
Л(
01:51
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Хм, что-то я потерял (не могу найти) своё уведомление о переносе сроков выпуска v0.13.1.
Видимо подумал, но не сделал.
Исправляюсь.

В конце июня на тестах Erigon было замечено две проблемы, поэтому я принял решение отложить выпуск пока ситуация не проясниться.
На текущий момент не удалось воспроизвести ни в тестах, ни в среде позволяющей проанализировать ситуацию детально (под отладчиком).
До понимания причин выпуска не будет.
02:02
Пока идут попытки воспроизвести упомянутые выше проблемы я добавляю запланированные возможности, которые, в зависимости от ситуации, войдут либо в v0.13.1 либо в следующую версию (v0.13.2).

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

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

В целом, "парковка транзакций" является еще одним средством обхода проблемы "долгих чтений" в дополнение к Handle-Slow-Readers.
СМ
05:55
Сергей Мирянов
In reply to this message
Вот
🤝
Л(
Benni invited Benni
Юрий Сурнаев invited Юрий Сурнаев
13 July 2024
В
14:11
Виктор
День добрый. Можно ли в одной транзакции в одном потоке открыть несколько курсоров на разные map_ handle в режиме readonly?
СМ
14:12
Сергей Мирянов
можно
👍
В
Л(
14:27
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Хм, только похоже я потерял часть правок создающих её описание. Сейчас поравлю.
18 July 2024
A
13:43
Alexander
осторожно, провокационная статья

https://habr.com/ru/articles/820591/
Л(
14:21
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Эту статью уже раз 5 показывали/обсуждали.

Она не провокационная, а скорее странная — трое студентов внезапно узнали как работает mmap и решили поделиться "открытием" в модно-молодежным стиле.

Если серьёзно, то тут просто выбор средства/инструмента под задачу/сценарий.
Почти классическая "дилемма" между микроскопом и молотком.

Абсолютно очевидно, что mmap() обеспечивает нулевые накладные расходы при чтении закешированных данных в памяти, но в замен добавляет расходов на подкачку этих данных с диска.
Соответственно, в сценариях когда много чтений и горячие данные помещаются в ОЗУ, mmap вне конкуренции.
И наоборот, если много обмена с диском, то только io_ring.

Остальное в статье — "разведенная вода" и даже почти неверная (как минимум устаревшая) информация.
😁
YS
VS
LP
A
14:31
Alexander
Да, там если присмотреться - то объективно нет сравнения сценария рандомного чтения данных из только памяти, без похода на диск, 4.1 специально настроили в сценарий 95% промахов кеша с походом на дисковую подсистему, т.е. подгоняли эксперимент к "выводам".
A
14:48
Alexander
с другой стороны - интересный вопрос, пока сугубо теоретический

если есть задача последовательно перебрать все записи в базе данных, которая не вмещается в RAM, то есть ли возможность сделать это без отображения в mmap()?
типовое применение - бекап, мы включаем режим O_DIRECT и не вымывая кеш данных (это важно) просто пробегаемся по всем записям?
14:51
в Oracle, к примеру, есть режим parallel query - там как раз реализуется массивно-параллельная обработка данных без похода в разделяемый буфер данных в памяти (он небесплатный), т.к. делается checkpoint на дисковую подистему, а потом несколько процессов работают только в режиме O_DIRECT чтения данных, минуя все конкуретные моменты доступа к памяти.
оно и само по себе дает буст на hashmap джойнах, и не вымывает кеш для OLTP транзакций
15:02
mdbx_env_copy() - это, к примеру, несколько не про то что нужно, интересует вопрос как снять логический бекап, ну или записи сразу переложить в новую копию базы, но в уже упорядоченном по ключу виде (перебирая их курсором в источнике фоном, но не вымывая кеш)
Л(
15:06
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Для любой задачи нужно выбирать соответствующие ей "микроскопы" и "молотоки".

Для того что вы описали лучше всего подходят колоночные БД.
Если смешать с OLTP и в масштабах "не помещается в ОЗУ", то будет пилить диски и спотыкаться в блокировках.

Если мы уходим от mmap(), то встаёт вопрос кеширования.
Это просто пока у вас один процесс, либо есть некий выделенный "серверный процесс".
Но в случае libmdbx (рой равнозначных процессов работающих с БД) такой кэш может быть либо не-разделяемым, либо хрупким (из-за подверженности ошибкам со стороны внешнего/чужого кода).

Технически в libmdbx можно реализовать использование mincore()+O_DIRECT внутри mdbx_env_copy().
При этом не особо важно перекладывать страницы целиком или по-записям, так как обход страниц все равно идет в порядке ключей и в глубину (aka deep-first).

Если сделаете, то приму коммиты после review.
Обсуждать заказную разработку я пока не готов из-за занятости/загрузки.
A
15:13
Alexander
Про колоночные СУБД да, это верно подмечено. Сценарий MDBX - это 0-day, т.е. оперативные данные текущего дня, для терабайтов исторических она не сильно удобна (мягко говоря).
Но нам пока такое и не требуется, исторические мы просто в Oracle складываем.

Просто интересно было - планируется ли реализовать режим чтения без mmap(), или может быть он уже есть но мы не нашли в исходниках-документации.
Л(
15:19
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Алгоритмически это просто, но технически историческая структура libmdbx не удобна для таких доработок.
Поэтому делать "просто так" не рационально.
Для такого напрашивается сделать движок с нуля, на C++ или Rust, предусмотрев максимуму для подобной кастомизации.
Но пока просто некогда.
👍
VS
A
15:19
Alexander
Кстати, если совсем набраться наглости с расспросами :) А не было ли замечено на просторах интернетов поколоночного движка СУБД, но с режимом доступа к данным через mmap(), в read/only?
Что-то вроде c-store, но как LMDB/MDBX? Цель - просто иметь возможность строить ORM как и c MDBX, но с произвольной глубиной в историю.
15:23
В теории это могло бы выглядеть как - имеем ключ - за данными сначала идем в MDBX, не нашли - идем в некую колоночную СУБД. С курсорами аналогично - открываем два курсора и перебираем диапазоны параллельно, сливая результаты. А изменения пишутся всегда сразу в MDBX, и потом периодически переносятся из MDBX в эту историческую поколоночную.
Л(
15:47
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Ну вы фактически описываете LSM и тот-же ClickHouse.
Проблема же с удалениями и апдейтами для "вчерашних" данных, которые уже осели на дно LSM.
В "принципе" оно как-то работает, то упирается в загрузку дисков, а иногда и в ЦПУ из-за расжатия/сжатия данных.

Использование mmap() тут выглядит заманчивым, но легко может создать еще один слой проблем из-за необходимости отслеживания старых снимков/файлов, вплоть до принудительного отстрела транзакций/процессов.
Т.е. при влияниях и/или модификациях внутри основной/большой БД вам нужно не трогать используемые/читаемые данные, а с другой стороны нельзя допускать чтобы таких старых кусков становилось много.
При этом использование mmap обесценивается при чтении холодных данных (много page faults) и не рационально использующего их процесса.
👍
VS
A
16:00
Alexander
In reply to this message
Тут да, эти идеи совсем не новы. Поправка только на уникальную особенность MDBX - возможность иметь единое адресное пространство приложения и СУБД, без обязательного сетевого обмена. С прямыми ссылками на данные. Сlickhouse вроде как нельзя относительно просто слинковать как библиотеку.
AS
16:47
Alex Sharov
In reply to this message
https://datafusion.apache.org - работает на arrow - который колоночный mmap
👍
VS
A
16:56
Alexander
Apache Arrow defines a language-independent columnar memory format for flat and hierarchical data, organized for efficient analytic operations on modern hardware like CPUs and GPUs. The Arrow memory format also supports zero-copy reads for lightning-fast data access without serialization overhead.


да, спасибо за наводку, последнее звучит прямо многообещающе
AS
17:00
Alex Sharov
In reply to this message
А datafusion - это в теории фреймворк для создания OLAP баз (с sql интерфейсом)
👍
VS
A
17:08
Alexander
datafusion выглядит прямо как, простите, tarantool :) А вот коммунити и тулсет вокруг apache arrow выглядит да, многообещающе, надо будет поспрашивать коллег, чем он хуже (лучше), чем kdb+
Л(
17:16
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Насчет как выглядит не знаю, но сделан тарантул правильно.
👎
VS
A
17:30
Alexander
Visual FoxPro тоже сделан правильно, правила это относительное понятие :)
По факту же оба комбайны с opinionware, и с типовыми досадными заблуждениями-анахронизмами вроде описания схем данных (полей, таблиц, типов) в виде строковых литералов, с проверкой этого всего только в runtime и т.д.
Оно конечно понятно, что тестами можно покрыть все что угодно, но хочется чтоб можно было в IDE просто написать invoice.client. нажать Ctrl+Space и сразу увидеть к примеру, .address, .tin, .name, .phone, но... нельзя. Или уже можно (не сочтите за троллинг)? ну или invoice[i].quantity ...
Л(
17:41
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Скорее не правила, а задачи.
А дальше возникает давление со стороны внутри-корпоративной аналитики и менеджмента, поэтому 90% решений прогнуты под потребности/задачи, которые были актуальны на момент их создания.
Если говорить, про Тарантул, то там действительно хорошо сделаны многие вещи, но при этом есть прогибы, о которых вы говорите.
Поэтому на уровне представления данных там действительно (как мне кажется) не всё хорошо.

Кстати, предлагаю посмотреть из интереса на https://gitflic.ru/project/erthink/libfptu
Схема данных там пока не прикручена, но мне всё равно будет интересно мнение со стороны.
👍
VS
A
19:30
Alexander
Если прям первое впечатление - то внесение информации о типе данных каждого поля внутрь каждой записи - это избыточно. Видимо навеяно ProtoBuffers, FlatBuffers и подобными реализациями.

А вот что хотел бы от подобного типовой (1С подобный) прикладник, т.е. как совместить Key/Value (и Storage, и IPCs) и заодно получить нормальный строго типизированный ORM с возможностями LINQ - можно рассказать, там правда ничего военного, но не уверен, что это тут не офтопик.
Л(
20:06
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Это не совсем то, и не совсем для того, о чем вы подумали.
Информация от типах не потому что нужна, а потому что не мешает.

В целевых сценариях этих кортежей много опциональных полей, т.е. из 50-200 разрешенных схемой может присутствовать 30-50, причем многие переменной длины (строки).
Поэтому требуется компактный индекс, чтобы быстро найти нужное поле и получить смещение к его значению.
Соответственно, для полей нужны какие-то идентификаторы, и тогда можно выделить несколько бит под машинный тип данных, чтобы иметь возможность заглянуть в записанные данные не имея схемы.
👍
VS
VS
20:26
Victor Smirnov
Да, если хочется свободно-структурированные документы в качестве записей, то память придется делать тегированной.

Свободное структурирование, даже если непосредственно не нужно для логики приложения, будет полезно для инкрементной эволюции схемы. Что очень любят аналитические системы.
20 July 2024
G
00:48
Giulio
Hey, I am trying to analyze an MDBX Database. My database seems to have 24 GB worth of pages allocated

mdbx_stat v0.12.9-16-gfff3fbd8 (2024-03-06T22:58:31+03:00, T-c5e6e3a4f75727b9e0039ad420ae167d3487d006)
Running for gg/chaindata/...
Environment Info
Pagesize: 8192
Dynamic datafile: 24576..13194139533312 bytes (+16777216/-33554432), 3..1610612736 pages (+2048/-4096)
Current mapsize: 13194139533312 bytes, 1610612736 pages
Current datafile: 14143193088 bytes, 1726464 pages
Last transaction ID: 456
Latter reader transaction ID: 446 (-10)
Max readers: 32114
Number of reader slots uses: 7
Garbage Collection
Pagesize: 8192
Tree depth: 1
Branch pages: 0
Leaf pages: 1
Overflow pages: 187
Entries: 2
08

But they are nowhere to be found within the tables, the closest I get is the table below:

Status of CommitmentVals
Pagesize: 8192
Tree depth: 4
Branch pages: 2911
Leaf pages: 481699
Overflow pages: 0
Entries: 6476355

The rest of the DB is really small tables who do not get to even 1 GB worth of bytes stored. any idea where this mysterious 85% of the pages are allocated at?
Л(
11:38
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Please run mdbx_chk -vvv path-to-db to unsure that are no pages lost during alloc/free, etc.

Then you can take look to a "Page allocation" section in the mdbx_chk output, and ask here if in doubt.

Next you can run mdbx_chk -vvvvv (i.e. more v to verbosity) for "page filling" and/or "length density" of k/v statistics, etc.
G
14:15
Giulio
In reply to this message
page allocation here does not seem to still add up
14:15
the sum of the page being allocated is not near the sum of the individual tables.
Л(
14:21
Леонид Юрьев (Leonid Yuriev)
In reply to this message
The mdbx_chk check this, actually it checks twice (via sum and then by bitmask) to verity itself logic.

So if mdbx_chk says "no problems", then it is.
14:24
In reply to this message
Seems you have wrong assumptions of such sums.
Please dig the source code of mdbx_env_chk() and internals.
G
14:30
Giulio
probably I do
22 July 2024
Л(
17:53
Леонид Юрьев (Leonid Yuriev)
В ближайшие дни планируется выпуск libmdbx v0.12.11 (на основе стабильной/старой ветки).

Основное исправление (цитата из ChangeLog):
 - Исправление для ОС Windows нарезки `FILE_SEGMENT_ELEMENT`.
Похоже что был потерян коммит входе работы над оптимизацией пути записи
на диск в ОС Windows. В текущем понимании, вероятность проявления ошибки
достаточно низкая, так как выявлена она была синтетическими тестами в
ходе других доработок, а соответствующих сообщений/жалоб не поступало. К
повреждению БД ошибка не приводила, так как сбой происходил до записи
данных с возвратом `ERROR_INVALID_PARAMETER` из системного вызова, т.е.
либо ошибка не проявлялась, либо транзакция не фиксировалась.

Остальные изменения см ChangeLog в ветке stable.
👍
СМ
СМ
17:56
Сергей Мирянов
• Использование \n вместо std::endl в C++ API при .

Наверное что то пропущено
Л(
17:57
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Ага, поправлю. Спасибо.
23 July 2024
A P invited A P
Л(
17:47
Леонид Юрьев (Leonid Yuriev)
Камрады, а кому нужна, вот прям требуется поддержка Windows ?

Хочется открутить это пятое колесо, чтобы меньше тратить времени.
СМ
18:21
Сергей Мирянов
мы пользуемся, и пользователей под windows пока больше
Л(
18:22
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Ох, принято
b
18:30
basiliscos
In reply to this message
я юзаю под вин
🤝
Л(
A
18:31
Alain
In reply to this message
We use it as well in development, not production
🤝
Л(
AK
18:54
Ant Kyt
In reply to this message
Windows — forever,
Linux — forever!
VS
18:55
Victor Smirnov
In reply to this message
Вражеская ОС, в прямом смысле. Надо откручивать в целях национальной безопасности.

(шутка)
ИМ
18:55
Илья Миχеев 🍃
In reply to this message
ноды некоторых клиентов эригона вроде на винде крутятся
Л(
19:20
Леонид Юрьев (Leonid Yuriev)
In reply to this message
В этом очень мало рациональности, только если "назло бабушке".

Технологически в Windows очень плохо в коде NTFS, от дизайна до алгоритимов и структуры. Как это у них случилось/получилось -- отдельная тема.

Но КПД там по ЦПУ меньше 5-10%. Поэтому linux выгоднее просто по затратам электроэнергии.

Могу предположить что эти узлы на Windows либо для тестирования Erigon и подтверждения кроссплатформенности, либо усердствует пара энтузазистов.

+ Чтобы на Windows было хуже на %, а не в разы, нужно переходить на использование raw volume, т.е. исключать хотя-бы файловую систему.
VS
19:32
Victor Smirnov
In reply to this message
Так всё плохо?
AK
20:02
Ant Kyt
In reply to this message
Иногда выбор ОС является историческим.
Вот есть парк серверов с виндой — и всё.
Переводить весь зоопарк на Линукс только
ради одной либы никто не будет.

Какая бы она ни была замечательная.
VS
20:04
Victor Smirnov
In reply to this message
А что, сейчас мало причин переводить на Linux?
AK
20:53
Ant Kyt
In reply to this message
Недостаточно.
VS
21:00
Victor Smirnov
In reply to this message
Ну, кто ж вам доктор тогда.
AV
21:38
Artem Vorotnikov
In reply to this message
А если перевести их на WSL?
21:38
In reply to this message
Тот же вопрос
СМ
21:40
Сергей Мирянов
In reply to this message
Если я правильно понял вопрос, то у нас не сервис, а встроенная бд, софт десктопный.
AV
21:40
Artem Vorotnikov
In reply to this message
Если говорить про эфировские ноды, на винде их не держит практически никто
21:41
In reply to this message
Десктопный софт запускать из-под WSL?
СМ
21:42
Сергей Мирянов
In reply to this message
Опыта нет, не уверен что так можно
AV
21:42
Artem Vorotnikov
@erthink как вариант, открутить поддержку винды в отдельную LTS-ветку
21:42
In reply to this message
Гуёвые приложения WSL поддерживает
СМ
21:46
Сергей Мирянов
In reply to this message
Спасибо, изучу вопрос
Л(
22:11
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Тут дело в другом.

Использование libmdbx оправдано/рационально в подходящих сценария (не противопоказанных сценариях) и при необходимости достижения некоторой производительности, когда дело доходит до "экономии на спичках".

+ Но с Windows это плохо соотносится.
Логично либо перейти на Linux (чтобы было быстрее), либо перейти на условный SQLite (чтобы меньше заморачиваться).
22:15
In reply to this message
Это тоже накладно по-времени и усилиям.

Буду спрашивать раз в год ;)
VS
22:20
Victor Smirnov
In reply to this message
А чего не перевести поддержку Win на платную основу?
AK
22:24
Ant Kyt
In reply to this message
В SQLite и им им подобных "якобы БД"
главная проблема — невозможность одновременного
доступа из разных процессов.

Даже тривиальный сценарий "1 писатель — много читателей"
становится головной болью.

Обобщенно можно сказать,
что СУБД с доступом по TCP упираются либо в производительность движка,
либо в производительность TCP.
Либо не являются "СУБД" в первичном понимании этого термина.


Решением являются Memory Mapped Files.
И Ваша замечательная библиотека,
как один из представителей этой семьи.

П.С. В Windows их скорость работы тоже хорошая.
Даже если они проигрывают на нижнем уровне.
VS
22:36
Victor Smirnov
In reply to this message
MDBX — транзакционная БД, а не аналитическая. У неё режим управления памятью не любит долгие транзакции. И, в целом, система типов не поддерживает распараллеливание. Разделять её между процессами есть смысл только если нужно между этими процессами орагнизовать какой-то очень быстрый IPC с большим трафиком. Типа очереди.
AK
22:40
Ant Kyt
In reply to this message
Именно.
"Организовать быстрый IPC".

Как и сказал, для меня основной сценарий:
один (или лучше несколько) писателей,
много читателей.

Читатели обычно знают ключ записи, которую ищут,
поэтому долгие транзакции типа "полный перебор"
практически никогда не выполняются.
👍
VS
b
22:40
basiliscos
обидно, если суппорт винды дропнется.
VS
22:41
Victor Smirnov
In reply to this message
Помогите проекту, и не дропнется. Но это не точно)
28 July 2024
Кемаль Ататюрк invited Кемаль Ататюрк
КА
12:48
Кемаль Ататюрк
In reply to this message
SQLite это однопользовательская БД, изначально, по архитектуре, есть обходной путь, когда один единственный пользователь становится поочередно и читателем, и писателем, это тупиковый путь. Вообще не используйте SQLite если вам нужно что-то писать и читать одновременно. Используйте, в однопоточных приложениях без асинхронных вызовов. Ключевое слово - асинхронность. Не надо в запорожец ставить 2й,3й,4й двигатель и синхонизировать их ременной передачей на общем валу, лучше поставьте 4 двигателя в электромобиль, по одному на колесо и общую MODBUS шину с интеллектуальным контроллером рекуперации и управления системами ESP, чувствуете разницу, в подходах? и там и там, линейное увеличение числа потоков дает разный результат, то же самое с SQLite и libmdbx
12:56
In reply to this message
А я на gitverse перешел, кстати, там напрямую ssh не поддерживается, требуется ход конем, могу показать, мое репо тут, пока балусь, но в принципе, интересно поиграться с libmdbx, в том числе, просто libmdbx - хороший проект, как я думаю:

https://gitverse.ru/default-writer
DH
14:34
Dvir H
I have a few questions about the meaning of some fields in the MDBX_commit_latency struct.
What is the meaning of work_counter?
What is the meaning of work_rsteps?
Does work_majflt also include minor page faults?
КА
14:53
Кемаль Ататюрк
In reply to this message
/ \brief Количество итераций поиска внутри GC при выделении страниц ради данных пользователя. */

/
\brief Nuber of iterations of searches inside GC on page allocations for user data. */
DH
15:09
Dvir H
In reply to this message
Thanks for the answer. I don't understand what it means. What is iteration? Reading one entry in the free list table?
AA
15:19
Alexey Akhunov
In reply to this message
When one (or more) consecutive pages need to be allocated, first MDBX tries to find big enough range of recycled pages in GC list. To do it, it searches, by loading parts of GC list into memory and looking through them. Iteration by iteration. Success is not guaranteed, so it may choose to abort after certain number of iterations and allocate at the end of db file (thus expanding the file), to keep commit time from becoming unpredictably long
👍
DH
29 July 2024
Л(
15:36
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Камрады, месяц назад я писал про обнаруженную проблему, потом уточнял.

Краткий статус:

1.
Проблема с зацикливанием/зависанием в mdbx_cursor_put() не подтвердилась.
Всё что было замечено — большие затраты на внутренние проверки при включенной отладки (MDBX_DEBUG) и включенном режиме MDBX_WRITEMAP (при этом ведутся списки "грязных" страниц и в режиме отладки существенно увеличиваются затраты на их проверку).

2.
Проблему с падением в mdbx_cursor_del() за месяц воспроизвести не удалось.
Тут стоит пояснить:
- Еще весной были переделаны курсоры именно для предотвращения подобных падений;
- Сами падения могли быть из-за наложения неверного использования API на исторически присутствующее UB в состоянии курсоров после некоторых операций;
- Буквально, падения могли быть при неверном использовании курсоров после неудачных операций поиска.

По имеющейся информации, добавленный предохранитель (возврат MDBX_ENODATA) позволил выявить несколько потенциально опасных мест в Erigon.
Поэтому растет подозрение что не-воспроизводимое падение было в некоторой "старой" версии до доработок, которая из-за человеческой ошибки была спутана с актуальной версией включающей доработки.

--

Если не будет какой-то новой информации, то выпуск v0.13.1 будет в начале августа.
👍
w
A
5
КА
30 July 2024
justice4 invited justice4
КА
11:43
Кемаль Ататюрк
а я вот думаю, а может быть, мне заняться UB в mdbx_cursor_*, какой-то бекграунд на C есть, может быть удастся воспроизвести, или там все глухо? а code codeverage есть для проекта, может-быть какой-то кейс не покрыт?
Л(
12:35
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Вы что-то не верно поняли.

UB (undefined behavior) было не из-за плохих/некорректных конструкций в коде, а из-за того, что (с точки зрения пользователя) состояние курсора могло быть недетерминировано в результате некоторых операций поиска при их неудачном завершении. А если после такой неудачной операции следовал вызов mdbx_cursor_del(MDBX_CURRENT) или mdbx_cusor_get(MDBX_CURRENT), то могло происходить падение.

Сейчас реализация курсоров уже переделана и в подобной ситуации будет возвращаться ошибка.

--

С code coverage ситуация "стакан наполовину полон".

Тесты "простукивают" все основные пути выполнения, но есть три фундаментальные проблемы:

1. Актуальная структура и наполнение конкретной БД является частью состояния.
Поэтому количество возможных вариантов/путей выполнения крайне велико, а линейная метрика покрытия даже при 100%, на самом деле может покрывать крайне незначительную часть.
Решается это только стохастическими тестами и бесконечным тестированием (неделями-месяцами, по нескольку десятков инстанций).

2. В случае libmdbx у нас рой взаимодействующих процессов, со своим собственным состоянием, плюс состояние внутри ядра ОС.
Поэтому к первому пункту добавляется состояние процессов и ядра ОС, плюс связанные с этим вероятностные процессы (lockfree, асинхронная обработка записи/чтения и изменения размера БД).

3. Есть пути/ветвление, добиться выполнения которых в тестах очень трудно/волокитно.
Необходимый объем тестов растет полиноминально.
Тут при желании вы можете помочь — как минимум просто тестировать ваши собственные сценарии использования.
КА
1 August 2024
A
03:29
Alexander
In reply to this message
для библиотек вроде MDBX наличие как можно более широкой кроссплатформы имеет дополнительный бонус,
к примеру можно отловить специфические баги или race conditions на оптимизациях, UB от operation reordering, которые могут не проявляться на linux/clang | gcc, но иметь место для MSVC или sunstudio, у нас были подобные кейсы, хоть и крайне крайне редко
VS
03:38
Victor Smirnov
In reply to this message
не окупает оно себя
Л(
13:05
Леонид Юрьев (Leonid Yuriev)
Помогаем бойцам и госпиталям.
Регулярно. Оперативно и адресcно, с личным участием.

Карта “МИР” для переводов 2202208129956448 (Валентина В).
Отчеты, благодарности и всё актуальное в ТГ-канале https://t.me/Volontior31RU.

Желающие отблагодарить за libmdbx могут помечать переводы "за mdbx" и т.п.
A
16:09
Alain
I am looking to see if there is a solution to have replication. I have researched some of the messages here, but I have not found a clear answer. I saw that there seems to be some replication with libfpta.

My use case is that when running in the cloud, I would like to be able to use local ephemeral storage that is much faster but to provide replication to external block storage that is more durable. It would be great if it was almost real time, but the main goal is to have very regular snapshots, like every few minutes (optimally if there were changes). Can this be achieved ? I remember seeing messages about mdbx_copy being able to run against a live DB. For info, most db are from 2 to 32GB with from 25-75% full.
КА
16:13
Кемаль Ататюрк
In reply to this message
i think this it a good question, on my opinion
A
16:14
Alexander
DRBD?
A
16:37
Alain
Been looking a bit at that, quite interesting, but still not sure how applicable yet,
Л(
16:44
Леонид Юрьев (Leonid Yuriev)
In reply to this message
1. For now there is no replacation in libfpta, it is under development.
Basically it will not be a classical replication, but "content synchronization" like RFC-4533.
With multimaster support and guaranteed state auto-convergence without the cost of delta-journalling.
However, as a representation of the data, there will only be tuples (see libfptu).

2. Yes, you can use mdbx_copy tool or mdbx_env_copy().

3. DRBD or HAST my be a good choice, but... both are designed for live/hot replication, but not time-to-time backup/synchronization case.
Therefore in time-to-time backup scenario you should pause write-transactions to get a consistent copy of DB on a block-level.
A
18:00
Alain
In reply to this message
Thanks Leonid, will try that. To find out if data has changed, can I use lastTxnId and no need to worry about latterReaderTxnId ?
Л(
18:30
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Yes, you need only the lastTxnId.
👍
A
КА
21:07
Кемаль Ататюрк
In reply to this message
For postgres i found this: https://github.com/pg-redis-fdw/redis_fdw
👍
A
Л(
21:16
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Что-то из подобных FDW я пробовал — это всё PoC и некие наколеночные эксперименты, поддерживающие только очень узкий перечень сценариев и с кучей ограничений.
Малейший шаг в сторону и нужно пилить-и-пилить, примерно как "паровоз напильником".
Плюс накладные расходы на маршалинг и перелопачивание запросов.
В итоге всегда оказывалось проще убрать pg-прослойку и работать с foreign data напрямую, а не через PostgreSQL.
КА
A
21:20
Alain
In reply to this message
At this point I am favoring using mdbx_env_copy() with the test for changes. I did some basic tests with mdbx_copy and that fine. Saw that it uses warmup, but didn't feel that to be needed/appropriate for a running db in my case. Will do some real testing next week.
Л(
21:37
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Historically mdbx_env_copy() didn't accepting a txn as argument, but start read txn internally.

However, in your case, it is more convenient to first start a read transaction, check whether there have been changes by the transaction number, and then, if necessary, copy DB by passing txn as an argument.

I think I can quickly add such an API if you use it.
A
21:38
Alain
In reply to this message
I will surely use it, and maybe this gives me a compelling reason to get the Java bindings ready for the new upcoming version :)
🤝
Л(
2 August 2024
Л(
01:43
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Take look to the devel branch.
This is preliminary/draft version of mdbx_txn_copy2fd(), but likely it will work.

https://gitflic.ru/project/erthink/libmdbx/commit/e912b9d2389a2740403132b3b2fd3fe3354ba0ee
VS
04:06
Victor Smirnov
DRBD-like or any block-based physical replication is not considered being efficient for databases. IO traffic will be unreasonably high, so it makes sense only within a single host. Synchronous on-line logical replication is easy, MDBX just need to support deterministic (error-free) rolling back of the last commit, so we can run 2PC/3PC on top of it. Then just execute transactions on all targets. Asynchronous logical replication may be even simpler, we may not need a rollback for that.
AS
05:01
Alex Sharov
In reply to this message
mdbx has 3 meta-pages
VS
05:04
Victor Smirnov
In reply to this message
Ideally, implementing an error-free rollback of the last commit should be easy in this architecture. But I'm not sure how it's aligned with MDBX's specific memory management algorithm.
КА
08:53
Кемаль Ататюрк
In reply to this message
as i understand, mdbx is ancestor of mdb, which effectively keeps immutability of data structures, so i hardly imagine in that therms how it could be achieved, as it is a memory mapping technique, so if we keep whole tree clone, so rolling back is just another operation reversing previous commit (like in git) so we could possibly do a rollback but we do not know when this commit will be taken cause rolling back takes time i believe, and it possibly became a nightmare for continuing normal operations at the same time, i could be wrong but keeping in memory reverse commit which does error-free rolllback is just the same as doubling memory footprint in a blink of the eye cause it still could lead to make modifications from the leaf node till the root node of the whole tree... may be i do not understand the theory, sorry about that
AS
08:59
Alex Sharov
Switch meta-page doesn’t take time.

Each meta page point to own “clone” of btree, but “clone” keeps only changed pages - un-changed pages are shared.
Л(
09:21
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Виктор, всё проще.

В MDBX (как и в LMDB) транзакция фиксируется записью/обновлением мета-страницы.
Поэтому необходимо и достаточно чтобы читатель видел записанную/обновленную мета-страницу не раньше всех остальных страниц измененных/записанных в ходе транзакции.

Соответственно, при по-блочной репликации достаточно чтобы на клоне охранялся/воспроизводился порядок операций записи блоков.
В штатном режиме работы DRDB/HAST так и будет — изменения реплицируются по мере их поступления, а при необходимости на мастере запись приостанавливается.

Но в режиме периодической (time-to-time) синхронизации, равно как и при первоначальном формировании реплики, необходимо чтобы мастер приостанавливал запись на время копирования блоков на реплику.
👍
VS
A
12:15
Alain
In reply to this message
I have a few questions about the new flags. Why would I want to use MDBX_CP_DONT_FLUSH (or not)? As for MDBX_CP_THROTTLE_MVCC and from what I read on mdbx_txn_park, it seems to be a recommended flag to use, am I right?
Finally I don't understand this condition:
if ((flags & MDBX_CP_THROTTLE_MVCC) != 0 &&
(txn->flags & MDBX_TXN_RDONLY) == 0)
return MDBX_EINVAL;
It seems to imply that if I use the throttle flag, I can't use a read only txn? Am I still asleep
Л(
12:31
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Yes, the documentation needs to be updated. I'll do it later.

All of these is just a some "songs and dances" to avoid troubles with long-lived read transactions:

- the MDBX_CP_THROTTLE_MVCC enforces using mdbx_txn_park()/mdbx_txn_unpark() during copy/write data;
- by MDBX_CP_DONT_FLUSH you could avoid flush (which could be a while) before transaction end.

Noted condition just returns EINVAL if MDBX_CP_THROTTLE_MVCC flags passed with read-write transaction.
A
12:34
Alain
In reply to this message
I was (or still am) asleep, but now I have my coffee ready :)
A
13:48
Alexander
In reply to this message
Там вопрос был чуть иначе. Если делать не физическую поблочную репликацию, а логическую, т.е. по сети слать не каждый измененный блок, а только вектор key/value для обновлений, то нужна недостающая фича вида "откатить последний коммит", для двухфазной фиксации транзакции.

Сейчас подобное можно достичь "финтом вида" - master ждет, пока на slave не произойдет фиксация транзакции, и только потом делает commit у себя.
Потенциально это может вызвать ситуацию, когда master зафейлится после commit slave и своим commit, и slave останется в позиции t+1, т.е. для slave нужно иметь опцию сделать rollback последней txn_id, если она ему прилетела еще раз от master (т.е. мастер рестартовал, потерял последний незакомиченый txn_id и прислал slave на репликацию уже совсем другой вектор).

Звучит как doable FR :)
13:53
Впрочем, мы сейчас подобное делаем чуть иначе - отдельно логгируем все транзакции в отдельный журнал (эдакий WAL) и при рестарте master просто еще раз из этого журнала попытается записать свою последнюю незакомиченную транзакцию. Но при возможности rollback_last_txn - от WAL в теории можно отказаться.
Л(
14:31
Леонид Юрьев (Leonid Yuriev)
In reply to this message
По контексту — весьма вероятно что я не понял вопроса, поэтому отвечал про DRBD

По поводу репликации и отката на пред-последний коммит — это всё очень хрупкое, с массой потенциальных UB из-за любой человеческой ошибки.
Для delta или replay-репликации, тем более для откатов реплики нужен сквозной/сшивающий контроль.

Например, если бы в mdbx был Merkle tree, то:
- на slave можно (было-бы) контролировать применимость дельты сверяя merkle-id до и после применения изменений (причем до коммита).
- аналогично на slave (было-бы) делать проверяемый автоматический откат, когда с мастера прилетает дельта от одного из предыдущих коммитов.

--

Некоторый вариант отката на предыдущий txnid в libmdbx уже есть, см. mdbx_env_turn_for_recovery().
Т.е. в некий нужным момент можно получить информацию о tnxid в мета-страницах и сделать откат посредством mdbx_env_open_for_recovery(TARGET_META) + mdbx_env_turn_for_recovery().

Однако делать из этого некое полноценное API отката достаточно проблематично, так как исторически сценарии использования libmdbx предусматривают взаимодействие роя процессов из которых никто не ожидает отката транзакций.
Например, что делать с читателем, который уже запустил транзакцию читающую MVCC-снимок, который мы хотим откатить?
А что если это была утилита mdbx_dump или mdbx_copy, либо реплика/backup на другой slave рангом ниже ?

Поэтому это плохой путь, к хрупкой системе, которая будет ломаться как только что-то пошло не так.
👍
VS
A
14:45
Alexander
В случае репликации slave находится в монопольном режиме write only, чтение из него не предполагается, только запись.
С другой стороны да, для всякой отчетности и аналитики есть сценарии использования slave в режиме только чтения, а они подобный "откат" возможно могут и не пережить.

Но при наличии отдельного WAL этот роллбэк реально малоактуален. А без WAL все равно не обойтись, backup/restore + recovery from WAL никто не отменял
СМ
14:54
Сергей Мирянов
мы сделали undo/redo log для отмены изменений и восстановления после сбоев (при условии что сбои не разрушили само хранилище)
14:55
плюс оказалось удобно реализовать сборку мусора на основе этого же лога.

все это конечно в user-space относительно mdbx
A
14:55
Alexander
c redo понятно, а про undo можно поподробнее?
СМ
14:57
Сергей Мирянов
там специфично для нашего проекта.
у нас используется cow для пользовательских данных, соответственно мы при undo записываем старый ключ на данные в пользовательскую структуру
КА
18:07
Кемаль Ататюрк
In reply to this message
спасибо за упоминание cow, почитал про cow в linux, выношу из всего этого вывод, такой, alias cp='cp --reflink=auto', потому что, reflink по умолчанию... выключен(!)
😁
VS
Pascal Berrang invited José Daniel Hernández
Л(
19:30
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Please pull/update (git history rewritten).
The bug was fixed and mdbx_txn_copy2pathname() was added.
A
19:40
Alain
In reply to this message
Will do this weekend
🤝
Л(
VS
22:47
Victor Smirnov
Для 2PC, который нужен для композициональности/шардинга, обычный commit разбивается на prepare и commit. prepare выполеяет всю работу, которая может дать сбой, сбрасывает буферы на устройство и т.д. Только он не публикует снэпшот в registry и читатели его не видят. Это делает commit.
22:49
Поэтому до момента окончательной фиксации транзакции читатели снэпшот не видят, и его можно безболезненно удалить просто сделав предпоследний снэпшот головным.
22:50
Я такие вещи у себя делал, но я сейчас 100% деталей не помню, как там с управлением памятью это всё бьется. Я помню, что какие-то дополнительные телодвижения всё-таки делал.
JH
23:52
José Daniel Hernández
Hi, we are investigating a slow down committing a transaction when pushing to a specific table that already has optimizations to use integer keys and to do append only operations. We have seen that this slowdown occurs in specific points in time (is not constant) and when we have pushed a considerable amount of data:
КА
23:53
Кемаль Ататюрк
could it be related to OS disk based issues?
JH
23:53
José Daniel Hernández
While also plotting the transaction statistics we have seen that 'merges' is what increases in the periods where the slowdown is seen:
23:54
To correlate the graphs, those graphs correspond to this transaction elapsed times:
23:55
Any idea on what could be happening with this table?
КА
23:56
Кемаль Ататюрк
could you please calculate average time of a transaction? maybe a long-polling transaction not closed at the start of this graph closes when timeout increases?
3 August 2024
Л(
00:01
Леонид Юрьев (Leonid Yuriev)
In reply to this message
1. Please show the MDBX_commit_latency from mdbx_txn_commit_ex() for a some "stucked" transaction.

2. Did you have large data items, i.e. longer than the mdbx_env_get_valsize4page_max() returns ?
How many ones?
JH
00:07
José Daniel Hernández
In reply to this message
Mmm, each point in the graph is for a single transaction that is opened and committed before going to the next one.
КА
00:08
Кемаль Ататюрк
In reply to this message
ok, thanks
JH
00:09
José Daniel Hernández
In reply to this message
Let me get this data and will come back to you. I think we might be getting into 2) and all of our data might be above that value but let me double check
Л(
00:11
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Please read about "Large data items".
AS
10:04
Alex Sharov
🔥
W
JH
7
👍
A
2
Л(
15:45
Леонид Юрьев (Leonid Yuriev)
В master-ветку пролиты как-бы последние правки перед формированием выпуска.

Кроме добавленных на днях функций копирования mdbx_txn_copy2pathname() и mdbx_txn_copy2fd(), там устранены мелкие ошибки/недочеты, а также сделан переход на использование термина "таблица" вместо "sub-database" (включая использование "database" в значении "sub-database" согласно традициям BDB).

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

Поэтому уже сейчас предлагаю всем попробовать ветку master в качестве v0.13.1.
👍
AV
КА
w
5 August 2024
JH
21:27
José Daniel Hernández
In reply to this message
Ok, just for completeness, we collected the latency data and it looks like this (above time that it takes to commit the transaction, below the whole latency in ms):
21:28
In reply to this message
Also we verified that for we're pushing data larger than what mdbx_env_get_valsize4page_max() returns by a factor of ~25x
21:30
In reply to this message
I guess our option here would be to reduce the amount of data we're pushing per what you mentioned ("Large data items")?
AA
21:33
Alexey Akhunov
you've got to clarify, when you say "pushing data larger than ... by factor x25", do you mean one record is larger than ... by factor of x25? Also, if these records are that large, do they regularly get overwritten? If yes (if you are overwriting or deleting records with large values), then it is likely this would cause longer commit times
21:34
also, the chart of latency as a function batch size probably does not take into account the fact that the worsening situation have "memory". Once you have created a long GC (garbage collection) list, it is hanging around until it is reduced somehow or the DB is manually compacted
21:36
In reply to this message
if you are inserting/updating records with large values, then the best solution is to make records with small values, so that any record comfortably fits into a DB page (4k, 8k, whatever you are setting), or increase the page size (though it has some side effects too on efficiency)
JH
21:39
José Daniel Hernández
In reply to this message
Every commit is one record and each record is larger than 25 times the value returned by mdbx_env_get_valsize4page_max().
Then for the other question, they are not regularly overwritten. We are pushing this data such that it can be available for readers afterwards (but in the plot above we're not doing any read operation, only the write).
21:41
In reply to this message
Got it. Thanks for the answer. Just for curiosity, how would one change the page size? I thought that was something coming from the OS
21:44
you can try to make page size, say 32x times larger, as an experiment, to see if you are seeing different picture
PB
21:56
Pascal Berrang
In reply to this message
So splitting the large value into several entries would make a difference?
AA
21:59
Alexey Akhunov
In reply to this message
Yes
22:00
since MDBX gives you atomicity of transactions, it makes sense to split/normalise the data. You may not be able to do it in other KV stores where atomicity is not that strong, but here this is the way to go
22:04
remember, when a large value (larger than page size) is committed, it has to be placed into consecutive pages. MDBX first tries to search for long enough series of consecutive pages in the GC list (and if the GC list is long, it is a lot of work), if that fails, it allocates at the end of the DB file. Obviously, if records fit into a single page, MDBX finds series of 1 consecuitive pages in GC list immediately (just take the first page), so this is ideal
👍
Л(
6 August 2024
AS
10:48
Alex Sharov
In reply to this message
Maybe your db becomes >> RAM? If yes - then remove from graphic part where db fits in ram.
Л(
12:31
Леонид Юрьев (Leonid Yuriev)
In reply to this message
1.
Yes.
Please RTFM the link I gave you earlier.

2.
The MDBX_commit_latency contains a sufficient number of fields to find out where delays occur and ones causes.
Please dig the docs, including using russian-to-english machine translation.
Then show latency values with all the parts.

3.
You should be interested to enable "GC profiling" by -DMDBX_ENABLE_PROFGC=1 build-time option.
This will provide/fills the gc_prof sub-structure of MDBX_commit_latency.
PB
7 August 2024
Л(
13:29
Леонид Юрьев (Leonid Yuriev)
При работе на NUMA с Linux 6.8 замечена проблема/регресс, что в текущем понимании относиться к ядру Linux, но не к libmdbx.

Сценарий проявления примено такой:
- с БД работают как миниум два процесса "А" и "Б";
- процесс "А" увеличивает размер БД (файла БД), задействует страницу в добавленном сегменте и фиксирует транзакцию;
- сразу после этого процесс "Б" начинает транзакцию чтения и падает по SIGSEGV при обращении к только-что добавленному сегменту.

Падение происходит в функции coherency_check() из-за того, что процесс "Б" не видит добавленный сегмент в своём виртуальном адресном пространстве.
Падение пока замечено только на NUMA.

Механика тут более сложная. Так как в таком сценарии у процесса "Б" должна быть ошибка страницы, которая должна привести к синхронизации его таблиц PTE связанных с отображенным в память файлом БД.
Соответственно, подсистема виртуальной памяти при обработке page fault в процессе "Б" должна заменить новый размер page folio у файла БД и отразить это в PTE-таблицах.
Сейчас этого не происходит, предположительно из-за регресса от каких-то доработок связанных с увтраненим блокировок в путях обработки page fault.

В результате, у процесса "Б" в PTE остаётся какой-то мусор, приводящий к SIGSEGV.
Отмечу, что при обращении за конец файла (но в пределах отображения) должен генерироваться сигнал SIGBUS, но не SIGSEGV.
Л(
13:46
Леонид Юрьев (Leonid Yuriev)
Пока я наблюдаю за тестами и изучаю ситуацию.

Если текущее понимание проблемы подтвердиться, то механизм проверки когерентности mmap будет доработан, с тем чтобы исключать подобные падения.
Будет ли это в готовящемся выпуске v0.13.1 или следующей версии v0.13.2 — пока сказать не могу.
8 August 2024
Chef Dev invited Chef Dev
Л(
00:37
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Посыпаю голову пеплом, но не сильно ;)
Ошибка была у меня.
Исправлено.
Willian invited Willian
VS
01:29
Victor Smirnov
Привет, чатлане. Не сочтите за рекламу, так как опенсорс. На него даже санкции не действуют (но это не точно). Если кто помнит, то я тут участвовал в дискуссиях о базовых алгоритмах и структурах данных MDBX/LMDB. Теперь можно раскрыть, зачем не это надо было. Я работаю над программно-аппаратным фреймворком, основанным на персистентных структурах и их свойствах. В его составе есть различные CoW storage engines, как для аналитики, так и для OLTP. Вариант для OLTP основан на алгоритмах LMDB по управлению свободной памятью. Остальные используют для этого счетчики ссылок (ARC GC). OLTP режим еще пока не готов, руки не доходят. Моя среднесрочная цель будет запустить направление computational storage, которое очень сильно упростит дизайн вычислительных систем.

Проект не является конкурентом MDBX, мы находимся в разных нишах.
👍
Л(
A
06:09
Arthur (George)
Hello all, a question: if I have a large free RAM (64G/128G) and the writes/reads to mdbx is much smaller, then do I need to implement a cache layer in my application codes to improve the performance or not? Thanks
AS
07:00
Alex Sharov
In reply to this message
It's premature optimization
A
09:50
Arthur (George)
In reply to this message
emm.. Follow up question, do you think, for Erigon or other blockchain node like it, is it meaningful to implement a cache layer on the top of mdbx db? (similar to the clean cache/disk layer cache in Geth). Thanks a lot.
AA
09:57
Alexey Akhunov
We think it is not. Remember that performance of caching relies on how well assumptions about work load hold. If you don’t make any assumptions about the work load, you are unlikely to create caching solution, which works much better than some defaults, like the ones in OS page cache.
👍
A
Л(
11:04
Леонид Юрьев (Leonid Yuriev)
In reply to this message
В целом интересно.
Как-нибудь постараюсь посмотреть, но пока свободного времени примерно нет.

Предварительно пока отпугивают зависимость от Qt.
Насколько сильно/глубоко я пока не осознал, но это первое что увидел.
Л(
11:25
Леонид Юрьев (Leonid Yuriev)
@benevolent_bot, что точно мне хотелось-бы обсуждать в этом ключе — выработку некого API для встраивания условного движка MDBX в подобные фреймворки, но не просто для абстрагирования KV, а более полноценно:
- I/O для взаимодействия с диском/файлом;
- управление памятью, в том числе автоматическое включение кеширование и пулов ресурсов/буферов;
- подклейку к реактору, включая асинхронное I/O, освобождение буферов и завершение транзакций.
- ну и т.д.

#lego
Л(
12:06
Леонид Юрьев (Leonid Yuriev)
In reply to this message
It is always inconvenient for me to answer such questions, because these are the basic things of software engineering with an obvious answer = you should weigh the cost of support/maintaining the cache and the benefit you get in your use cases.

For example, we can consider two extreme characteristic cases:

1. Let you have:
- a DB with 1000K of key-value pairs;
- a 1 write TPS (transaction per second) which update 10K of kv-pairs;
- a 1000000 read TPS which need a sum/avg/rms of all values;
In this case you need a cache, sure.

2. Contrary:
- a 1000 write TPS which update 5-10 pairs randomly;
- a 1000 read TPS which reads 5-10 pairs randomly;
In this case you NOT need a cache, sure.

--

In MDBX, read transactions are fairly cheap and overhead is minimal.
Therefore, all other things being equal, on average for most usage scenarios, the cache is less useful compared to many other storage engines.
However, only you can weigh all the factors yourself and decide what to do.
A
13:18
Arthur (George)
In reply to this message
I agree and totally understand. We are doing some experiments - the results show the increased cache hit rates and however no overall performance improvment, which makes me confused for I am usually using other kinds of databases. Thanks a lot for you patient answers. 🙏
КА
🤝
Л(
VS
17:11
Victor Smirnov
In reply to this message
Datascope там использует Qt для UI. Но это временно. Потом будет обычная HTML5-вебня.
17:16
In reply to this message
Да, это имеет смысл. Там сейчас есть вариант Store над LMDB, но он чисто экспериментальный. Я даже не пытался завести его над MDBX, так как пока смысла нет. Но переезд на MDBX неизбежен, если этот вариант Store получит путевку в жизнь. Он интересен, в первую очередь для IPC. Иногда такое кейсы возникают, ради чего я этот Store и завел. Еще он будет использоваться для тестирования, так как так легче изолировать ошибки в контейнерах.
AA
17:28
Alexey Akhunov
In reply to this message
А ты планируешь потом выпускать такие железяки?
VS
17:31
Victor Smirnov
In reply to this message
Да. Но только не спрашивай, как. Технически мне это проще, так как я живу и работаю в США. Но как это конкретно будет сделано — пока туман. Скорее всего, это будут делать другие, более подготовленные к этому, люди. Я же будут заниматься софтовой частью.
👍
AA
VS
17:47
Victor Smirnov
Но, в целом, в кастомных железках тут основной смысл. CPU-центричная архитектура устарела и плохо масштабируется. Но дело в доступном софте, потому что без софта железо — просто дорогой кирпич. Вот я эту проблему закрываю. И я создаю спецификацию на железо, которое будет в фреймворке поддерживаться. А железо могут делать все, кто хочет и может.
9 August 2024
Antipublic One invited Antipublic One
Алексей (Keller) Костюк 😐 invited Алексей (Keller) Костюк 😐
АК
03:05
Алексей (Keller) Костюк 😐
Здравствуйте. Мы сейчас рассматриваем возможность перехода с LMDB на вашу MDBX. Подскажите, пожалуйста, есть ли в этом смысл, либо же это не целесообразно?

База у нас весит 2ТБ. Готовой реализации MDBX с курсорами под NodeJS нет, поэтому нам придётся в первую очередь написать библиотеку, а это займёт не один день. А учитывая размер базы, то и сама миграция займёт до двух недель.

После вашего совета мы решили проблему, удаляя мёртвых читателей после перезапуска NodeJS. Однако, последствия после того случая остались. А именно, freepages, который размечает 300 ГБ пустоты. Первое время вставка занимала +- 3 часа. Сейчас же процесс длится уже 5-й день и не может вставить даже одну строку. Проанализировав код, я понял, что идёт вечный "брут" внутри функции mdb_freelist_save
AS
03:12
Alex Sharov
In reply to this message
- что вас спасет от долгих вставок: увеличение размера страницы (у lmdb такой фичи нет).
- js библиотека от lmdb - скорее всего будет работать. Нужно будет добавить set_geometry функцию.
- про долгих читателей - магии не будет - но вроде в последней версии добавлено несколько методов для их отстрела
АК
03:16
Алексей (Keller) Костюк 😐
In reply to this message
Долгая вставка как раз вызвана большим freelist, который образовался из-за "зависшего" читателя. А он образовался из-за перезапуска ноды. После рестарта он не удалился и начал дальше расширять файл...
Л(
03:20
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Твоему теске я отвечу завтра. А вас спасает больше BigFoot feature, SIMD-сканирование и самые быстрые из известных реализаций двоичного поиска и слияния спискоа номеров страниц. Ну и т.п. ;)
АК
03:20
Алексей (Keller) Костюк 😐
Я уже нагородил костыль, который проверяет читателей при запуске. И запустил на фоне compact, чтобы убрать этот freelist.

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

Если же мы будем использовать MDBX, возможно у нас хоть появляется небольшая поддержка.
AS
03:25
Alex Sharov
In reply to this message
Да, но на 2Tb - в будущем будут и другие баги или новый тип данных - которые приведут к похожему эффекту. Победив читателей - вы все равно на это будете натыкаться.
03:37
BigFoot - да, фот эта фича тоже обязательна для медленного mdb_freelist_save

Про бОльший размер страниц:
- меньше страниц - меньше работы для freeList
- больше шанса что данные влезут на 2 страницы (а значит не вызовется медленная часть mdb_save_ )
- freeList и сам начитает работать на бОльших страницах - на которые влезает больше ID свободных страниц - лучше батч-процессинг фрилиста (больше батчи).
- меньше вероятность того что во фрилисте не найдется и придется наращивать базу

Мы тоже перешли несколько лет назад с lmdb - по причинам поддержки. Мы сделали BigFoot фичу в lmdb - и оно работало. Но поддержка - есть поддержка.

С разных сторон становится веселее.
VS
03:51
Victor Smirnov
In reply to this message
Тут просто отличная поддержка для опенсорсной БД)
АК
03:58
Алексей (Keller) Костюк 😐
Но если размер страниц больше, не получается, что больше обращений к диску?
У нас данные не превышают более 100 символов на одну строку. Поэтому, не знаю, нужно ли в этом случаи жертвовать размером бд и лишним I\O...

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

Также ещё существуют суббд, в которых находятся "индексы" на данные в главной в этом же файле. Получается, что если я вставляю хотя бы одну строку, которая размером 10 байт, то будет перезаписано 4 бд = 5 (глубина дерева) * 4 бд * (увеличенный размер страницы). По-моему это очень сильно увеличит I\O на диске. Наверное это имело бы смысл, если увеличивать размер страницы только для FREE_DB...

А вот по поводу остальных советов (BigFoot, SIMD) я мало чего понял... Я не сильно разбираюсь в конструировании баз данных.
04:01
In reply to this message
А лучше LMDB, MDBX врятли что-то существует... :)
Все остальные БД очень кушают либо ЦП, либо I\O.
VS
04:13
Victor Smirnov
In reply to this message
IO MDBX сильно кушает. Это всё-таки CoW, причем над MMAP. LSM поэкономнее будут — для SSD это имеет значение.
АК
04:22
Алексей (Keller) Костюк 😐
In reply to this message
Хм... Я как-то рассматривал LSM... Они хороши для записи, и когда много места на диске. У нас же уже осталось всего ничего. Да и скорость моментального чтения важнее, чем запись.
Для записи мы сделали костыль, в виде двух LMDB.
Одна большая главная бд, в которой хранятся все данные. Во вторую же записываются все данные, а в последствии они постепенно переносятся в главную.
VS
04:42
Victor Smirnov
In reply to this message
Зависит от характера нагрузки. Для CoW-дерева — да, будет в среднем больше. Для freeList — меньше, так как это список, а не дерево.
04:43
In reply to this message
Всё верно.
04:45
In reply to this message
Настоятельно рекомендую досконально понимать, как работает основная БД, от API и до как это всё контроллер SSD пережевывает. Время это много не отнимет, сама MDBX относительно небольшая, а вот жизни это сэкономит — немеряно)
04:49
У LMDB два больных места: большие value и долгоживущие читатели. By Design. Первую проблему Леонид у себя в MDBX немного починил и это стало BigFoot (если меня память не подводит), а вторая осталась и неизлечима.
АК
04:50
Алексей (Keller) Костюк 😐
In reply to this message
Ну вот с этим проблем нет, overflowPages равный нулю говорит сам за себя)
👍
VS
04:56
In reply to this message
А вот это да... Была проблема, о которой я не знал. Возможно это проблема библиотеки для NodeJS, которая не обрабатывала SIGINT (или подобное) и не закрывала за собой читатели. Тем более, что в ней даже не существовало биндинга на функцию проверки читателей...
(сейчас оба случая обрабатываю)

Но, в проекте данные по бд (включая индексные) ищутся не более 10 секунд. Поэтому, сейчас это не должно быть очень большой проблемой...
VS
04:57
Victor Smirnov
10 секунд — это нормально.
АК
04:59
Алексей (Keller) Костюк 😐
In reply to this message
А при каких значениях уже не норма?
VS
05:00
Victor Smirnov
Это надо или рассчитывать, или (лучше) бенчмаркать. Если freelist растет, значит или среднее время жизни читателя слишком высокое. Или кто-то завис (но это другой кейс).
АК
05:01
Алексей (Keller) Костюк 😐
In reply to this message
Мы вот сейчас с нашими гадаем... Что будет быстрее... Сортировка текущего freepages, или же compact...
(ради того, чтобы сжать бд пришлось даже просить админов сервера подключать внешний HDD (ибо SDD уже и так нет у них), дабы туда сжать её)
05:05
Получается мигрироваться на MDBX есть смысл хотя бы по двум причинам?
- Использование freepages с конца
- Возвращать обратно на диск freepages, которые находятся с краю
(если вдруг произойдёт опять такое ЧП с freelist, то эти пункты смягчат последствия)

Или это того не стоит?
VS
05:18
Victor Smirnov
стоит
АК
05:19
Алексей (Keller) Костюк 😐
хорошо, спасибо
VS
05:29
Victor Smirnov
Если бы у меня стоят вопрос, съезжать ли с LMDB на MDBX — я бы съехал не раздумывая. У последней на много лучше код. И если что-то с основным и единственным разработчиком случится (а мы знаем, что он — рисковый парень ;), то я бы мог подхватить этот код и вести его дальше сам. А вот код LMDB — нет уж, нет уж. Шедевр запутанности.
АК
05:31
Алексей (Keller) Костюк 😐
In reply to this message
Я ознакомился с её кодом... Не то, что был в восторге... Но яркие впечатления после этого остались xD
VS
05:35
Victor Smirnov
Ну и MDBX быстрее и лучше практически во всех сценариях. Единственный минус, что всегда можно напороться на что-то, что работало в LMDB, но глючит в MDBX. Но это, такое. Издержки профессии.
АК
05:40
Алексей (Keller) Костюк 😐
In reply to this message
Мне главное, чтобы DUPSORT работал) И опционально видеть стату...

Кстати, появился вопрос, а MDBX умеет выводить кол-во уникальных ключей для DUP из коробки? В LMDB entryCount выводит кол-во значений, но не уникальных ключей
05:43
И ещё вопрос... Какие популярные проекты/компании используют MDBX?

Про LMDB я особо тоже не в курсе. Знаю только, что Monero использует её
AS
07:51
Alex Sharov
In reply to this message
тут более неочевидная зависимость:
- больше страница - менше фрагментации данных на диске. т.е. при формально большем IO (mb/s) внезапно fsync может стать быстрее (т.к. меньше фрагментация)
- больше страница - меньше глубина дерева. т.е. и branch-страницы увеличатся и количество листовых страниц уменьшится. Меньше глубина - меньше IO.
- каждая страница меет немного метаданных. Меньше страниц - меньше метаданных - меньше IO.
- т.е. я думаю что IO возрастет - но не совсем так линейно как в вашей формуле. И скорость тоже может измениться неочевидно.
- там еще есть крутилка opt_merge_threshold - можно ее крутить увеличивая плотность данных на странице ценой более частых слияний полу-пустых страниц (вставки/удаления становятся дороже по CPU). Тоже может неочевидно на IO повлиять.
АК
07:53
Алексей (Keller) Костюк 😐
In reply to this message
Разве фрагментация касается SSD дисков?
VS
07:54
Victor Smirnov
In reply to this message
Еще как касается)
07:59
In reply to this message
Там, скорее всего, есть некий оптимальный размер блока, типа 8K или 16K, на котором IO и WA будут минимальны. А потом всё начнет расти. Больший блок однозначно лучше для аналитики. И хорошо, если формат позволяет работать с блоками разного размера для разных типов данных.
СМ
08:02
Сергей Мирянов
In reply to this message
иногда хочется такого в MDBX для разных таблиц - у нас есть одна таблица с жирными данными и там много overflow страниц, тогда как для остальных все норм (там маленькие данные)
AS
08:02
Alex Sharov
In reply to this message
всякое бывает. там кроме диска еще могут быть жители:
- бывают FS/OS которым нужно хранить маппинг между диском и памятью вашего процессора - меньше фрагментации - меньше метаданных этих (и их обслуживание дешевле)
- былвают FS со включенной компрессией - им тоже удобнее с бОльшими страницами работать.
08:03
In reply to this message
Леонид предлагал мне несколько лет назад делать и тестировать такую фичу. Но, нам было не надо.
VS
08:26
Victor Smirnov
In reply to this message
Конкретно в данном случае лучше не блоки переменного размера, а специальный формат b-tree, который позволяет иметь произвольный размер value, но чтобы всё в рамках блоков одного размера. Это будет довольно сложно, но можно.

А блоки переменного размера — это лютая головная боль для управления памятью. Это только для аналитики будет интересно. А MDBX всё равно не аналитическая БД.
Л(
09:21
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Доброе утро!

Вам тут уже понаписали, но давайте я отвечу как обещал.

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

2.
Для NodeJS были какие-то работающие биндинги на Github, но после блокировки/удаления я не слежу за этим.
Тем не менее, если есть работающие биндигни для LMDB, то сделать на этой основе поддержку MDBX достаточно просто (как мне представляется).
Сейчас в TODO есть пункт "Migration Guide from LMDB to MDBX" и его можно отработать на вашем сценарии, если вы просто будите записывать свои действия (со своей стороны, в этом случае я окажу приоритетную поддержку).
На уровне "рисования совы" миграция выглядит так:
- два раза полностью читаем mdbx.h и просматриваем декларации в mdbx.h++;
- механически переименовываем функции и константы заменяя: mdb_ -> mdbx_ и MDB_ -> MDBX_;
- добавляем поддержку недостающих функций и возможностей (пожалуй это основная часть работы);
- дорабатываем работу с курсорами, так как в MDBX все курсоры единообразно требуют явного разрушения;
- выгружаем данные посредством mdb_dump и загружаем посредством mdbx_load.

3.
В LMDB есть несколько архитектурных и алгоритмических проблем/недостатков.
Структура freelist (aka FreeDB или GC) и её обновление — одна из таких проблем.
В MDBX с этим намного лучше, но пока не идеально (запланирована переработке функции gc_update() — аналога mdb_freelist_save()).
Пока не известны сценарии где MDBX работает хуже/медленнее, либо не работает.
При этом есть сценарии где MDBX работает в 10-1000 раз быстрее, а LMDB не выплывает или работает неприемлемо плохо/медленно.

3.1.
Алгоритмически mdb_freelist_save() и gc_update() очень сложны, так как изменяют b-tree внутри которого хранятся списки свободных страниц, с использованием этих-же страниц.
Другими словами, тут присутствует рекурсия по данным, а само обновление может потребовать несколько повтором/итераций, которые в итоге должны "сойтись".
Сложность в том, чтобы это схождение происходило с минимальным кол-вом итераций, с минимальными накладными расходами (особенно в простых случаях) и работало во всех сценариях при любом содержании БД.
В LMDB функция mdb_freelist_save() относительно простая (с одной извилиной) поэтому хорошо работает только в простых/тривиальных случаях, а в сложных может работать неприемлемо медленно, в том числе зацикливаться до бесконечности.
В MDBX функция gc_update() уже адски сложная/запутанная, но еще есть сценарии/случаи когда она может зацикливаться (в текущем понимании это не затрагивает пользователей, так как проблемные сценарии крайне специфичны и приведут к возврату ошибки, а не бесконечному циклу).

3.2.
При больших транзакциях (с большим количеством удаляемых и/или изменяемых страниц) образует много отставных/retired страниц, номера которых необходимо помещать в GC/freelist.
Если транзакции действительно большие, то такие списки страниц могут быть очень длинными и не помещаться в одну страницу.
В таких случаях LMDB будет сохранять огромный список одним куском, что потребует нескольких соседних/прилегающих свободных страниц — в b-tree вероятность образования таких последовательностей быстро/экспоненциально убывает с ростом их длины.
Это порождает большую проблему см. "Large data items and huge transactions".
В MDBX же уже давно есть BigFoot, что полностью ликвидирует проблему больших транзакций, но для длинных данных(большие результата mdbx_env_get_valsize4page_max()) она пока остается.

4.
Для решения проблемы "зависших читателей" в master-ветке MDBX сейчас есть:
- механизм "парковки" читающих транзакций, что позволяет вытеснять мешающие переработке мусора транзакции;
- механизм "уведомления и отстрела" медленных/повисших читателей через обратный вызов в процессе-писателе;
- переработка мусора в LIFO-режиме, что нивелирует последствия увеличения БД и GC/freelist после "застревания" читателя.
09:21
In reply to this message
Планируется реализация не-линейной переработки мусора и явная дефрагментация, но я не хочу давать каких-либо обещаний по срокам.

5.
LMDB не проходит тесты, которые проходит MDBX.
Точнее говоря, несколько лет назад LMDB падала на тестах используемых для проверки MDBX.
После этого эти тесты были существенно доработаны, в том числе были найдены и устранены достаточно "заковыристые" ошибки, в том числе унаследованные от LMDB.

Сейчас мне не известны сценарии, где переход на MDBX создавал проблемы/глюки, но с двумя поправками:

5.1. При правильном использовании API и учета отличий.
Например, основная жалоба на MDBX на утечки памяти из-за не-разрушения курсоров самим приложением.

5.2. MDBX может возвращать ошибки там где у LMDB "все хорошо".
В MDBX много дополнительного контроля и проверок корректности использования API с возвратом ошибок в таких ситуациях.
А LMDB маскирует/игнорирует множество таких ситуаций, что приводит к сложно-воспроизводимым падения, повреждениям БД, потери и/или искажению данных.
👏
A
A
12:47
Alain
In reply to this message
It was a long time ago, but I would never go back and in those days we had a few catastrophic failures with LMDB that obliterated our DB. Never, ever happened with MDBX. That makes me sleep well at night.
👍
VS
Л(
A
13:22
Alain
In reply to this message
We have the same need and in our Java binding we do it with a cursor_get and a predicate. But I believe that you could use the newer mdbx_cursor_scan or variant to do just that.
N
15:33
Nekto
In reply to this message
В плохих сценариях SSD дают скорость случайного доступа (по 4кб) в ~50 раз выше чем на HDD (случайной записи ~80+ раз, но здесь у SSD есть нюансы когда много-долго пишем), поэтому они в типичных нагрузках когда много кусков кажутся серебрянной пулей и нет такой субъективной катастрофической просадки, просто в худшем случае вместо 700КБ/сек получается ~35МБ/сек (записи ~140МБ/сек), т.е. скорость в десятки раз меньше чем при линейном чтении (записи ~4 раза).
На NVMe скорости выше, но разница между случайной и линейной остаётся, причём отрыв только растёт.
👍
VS
П
15:56
Павел
In reply to this message
Hello, Alain! What binding library for Java are you using?
A
15:59
Alain
In reply to this message
We have maintained a Java binding library for many years, it's even listed in the official mdbx documentation. It is located at: https://github.com/castortech/mdbxjni
Also I'm working on updating it for the new upcoming release and should be available in the next week or two.
👍
VS
П
16:03
Павел
In reply to this message
Thanks! I tried it a while ago. For Windows I succeed but for Linux I was not able to launch my app (it can't load library). I'll be waiting for updates.
A
16:06
Alain
In reply to this message
For the current version we have not released the jar that include the so or dll. But we use it only under Linux for production (Windows is used only in dev at the moment)
16:09
Let me know if you want it. otherwise I'll make sure to not only update the source but provide jars for the new version
П
16:20
Павел
In reply to this message
It will be nice if you can provide jars for the new version.
👍
A
АК
18:11
Алексей (Keller) Костюк 😐
In reply to this message
Да. Там есть один модуль под Mdbx, но он крайне сырой. То есть практически ничего нет и под старую версию, где вы убрали пару флагов/функций. Думаю её возьму за основу, чтобы написать свой вариант.
18:12
In reply to this message
Но вот брать за основу модуль от LMDB себе дороже... Ибо там настолько ужасный код, что хочется плакать....
18:14
In reply to this message
А если одновременно читать и записывать с помощью APPEND? Быстрее же будет, чем промежуточный файл.
Или dump/load умеют в пайпинги?
A
18:15
Alain
In reply to this message
The API is extremely similar and that should be your focus IMHO. Only in the last few versions has it started to diverge a bit, mostly new stuff being added. I think the biggest difference is what Leonid mentioned about how to manage cursors, but that is the usage part, not the API itself and your binding. Up to about 1 year ago we still maintained the LMDB binding and that was never far off.
АК
18:17
Алексей (Keller) Костюк 😐
In reply to this message
Okay, I'll keep that in mind. Thanks.
10 August 2024
АК
03:28
Алексей (Keller) Костюк 😐
@erthink, какая-та беда с mdbx_load :(
03:33
mdbx_load
03:34
mdbx_stat
03:34
Л(
08:56
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Исходный код верный, но вы (видимо) плохо умеете в C и упускаете вполне очевидное.

Интересно же другое — как вам удалось получить mdbx_load не со скомпилированными машинным исполнимым кодом, а вроде-бы с исходным кодом из mdbx_load.c.
Ну или что-то подобное.
АК
08:57
Алексей (Keller) Костюк 😐
Скачал из репы
(Релиз)
Л(
08:58
Леонид Юрьев (Leonid Yuriev)
Хм, сейчас перепроверю что там.
АК
08:58
Алексей (Keller) Костюк 😐
In reply to this message
Ну я подправил и всё ок стало...
08:59
Единственное ещё были проблемы с размером пачки, поэтому я его тоже убрал... И миграция вроде пошла бодренько :)
Л(
09:00
Леонид Юрьев (Leonid Yuriev)
Хм, а что вы поправили?
Переменная prog определена как static в глобальном контексте.

А сообщения про usage() и -s выдает не компилятор, а shell или что-от типа этого.
АК
09:01
Алексей (Keller) Костюк 😐
In reply to this message
Создал аргумент в usage, а дальше передал в аргументы. Точно также, как и в других файлах (dump, stat...)
Л(
09:03
Леонид Юрьев (Leonid Yuriev)
In reply to this message
В mdbx_load переменная prog используется еще где-то.
Если вы её не присвоили/инициализировали, то что-то поломали.
АК
09:03
Алексей (Keller) Костюк 😐
In reply to this message
Вы скачайте сами последний релиз и проверьте :)
Там сразу не работает))
09:06
In reply to this message
Даже как-то не обратил внимания на это...
Л(
09:17
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Последний релиз 0.2.11, https://gitflic.ru/project/erthink/libmdbx/release/8b7b1bb7-435f-4451-a3eb-c865c56833fd

В описании релиза действительно была ошибка — предположительно из-за "продвинутого" редактора markdown ссылка на тарболл с релизом была битая (404).
Аналогично было и с предыдущим релизом 0.2.10.
Поэтому хорошо что появился повод перепроверить!

Но проблем подобных наблюдаемому у вас там нет.
АК
09:19
Алексей (Keller) Костюк 😐
In reply to this message
А да там была проблема со скачиванием. Я думал чёт не так сделал, поэтому добавил в ссылке tar вручную
09:20
In reply to this message
А последний 2.11) Кажется забыл вернуть вкладку)
09:20
Чуть позже проверю 2.11
Л(
09:22
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Ну это никак не влияет на наблюдаемые у вас странности с mdbx_load, и никак их не объясняет.

Попробуйте для начала просто make clean и затем make.
АК
09:34
Алексей (Keller) Костюк 😐
In reply to this message
Да чистая сборка помогла
👍
Л(
Л(
10:31
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Для разработки сейчас лучше использовать master-ветку, где уже есть новый функционал (см ChangeLog).

Чтобы получить набор файлов аналогичный релизу достаточно make dist.
АК
10:34
Алексей (Keller) Костюк 😐
А вот это реально нужная вещь... Я перерыл весь код LMDB и не находил... А тут буквально недавно... Благодарю)
🤝
Л(
13 August 2024
w
17:23
walter
I need help to use mdbx with systemd.

right now, when I run app1 in systemd service A, and app2 in systemd service B.

they will access same database, will block each other at mdbx_txn_begin_ex

how to fix this ?
AS
17:59
Alex Sharov
In reply to this message
Must be different PID.

Only 1 RwTx can exist at a time. Open RoTx.
w
18:12
walter
The process B use MDBX_RDONLY + MDBX_TXN_RDONLY. B block at mdbx_txn_begin_ex
18:23
The process A cgroup: 0::/user.slice/user-1000.slice/user@1000.service/app.slice/core.service

namespace

lrwxrwxrwx 1 podman podman 0 Aug 13 23:21 cgroup -> cgroup:[4026532807]
lrwxrwxrwx 1 podman podman 0 Aug 13 23:21 ipc -> ipc:[4026532713]
lrwxrwxrwx 1 podman podman 0 Aug 13 23:21 mnt -> mnt:[4026532711]
lrwxrwxrwx 1 podman podman 0 Aug 13 23:21 net -> net:[4026532715]
lrwxrwxrwx 1 podman podman 0 Aug 13 23:21 pid -> pid:[4026532714]
lrwxrwxrwx 1 podman podman 0 Aug 13 23:21 pid_for_children -> pid:[4026532714]
lrwxrwxrwx 1 podman podman 0 Aug 13 23:21 time -> time:[4026531834]
lrwxrwxrwx 1 podman podman 0 Aug 13 23:21 time_for_children -> time:[4026531834]
lrwxrwxrwx 1 podman podman 0 Aug 13 23:21 user -> user:[4026532710]
lrwxrwxrwx 1 podman podman 0 Aug 13 23:21 uts -> uts:[4026532712]

The process B cgroup: 0::/user.slice/user-1000.slice/user@1000.service/app.slice/cache.service

namespace

lrwxrwxrwx 1 podman podman 0 Aug 13 23:20 cgroup -> cgroup:[4026532807]
lrwxrwxrwx 1 podman podman 0 Aug 13 23:20 ipc -> ipc:[4026532713]
lrwxrwxrwx 1 podman podman 0 Aug 13 23:20 mnt -> mnt:[4026532711]
lrwxrwxrwx 1 podman podman 0 Aug 13 23:20 net -> net:[4026532715]
lrwxrwxrwx 1 podman podman 0 Aug 13 23:20 pid -> pid:[4026532714]
lrwxrwxrwx 1 podman podman 0 Aug 13 23:20 pid_for_children -> pid:[4026532714]
lrwxrwxrwx 1 podman podman 0 Aug 13 23:20 time -> time:[4026531834]
lrwxrwxrwx 1 podman podman 0 Aug 13 23:20 time_for_children -> time:[4026531834]
lrwxrwxrwx 1 podman podman 0 Aug 13 23:20 user -> user:[4026532710]
lrwxrwxrwx 1 podman podman 0 Aug 13 23:20 uts -> uts:[4026532712]


I get this info by add bash script before start the app:

#!/bin/bash

cat /proc/$$/cgroup > /tmp/core.log 2>&1
ls -alFh /proc/$$/ns/ >> /tmp/core.log 2>&1

exec ./main
18:26
I can see they are in same mount namespace 4026532711, but diff cgroup (I guess cgroup create by systemd) . what clould cuase the block ?

If I login by ssh , start 2 process from shell. then no problem
АК
18:28
Алексей (Keller) Костюк 😐
In reply to this message
What if we use a "screen"?
w
18:31
walter
I am test it now. seems also has problem with ssh. I will test more and feedback
18:45
2 ssh login session. or 2 tmux session, will block each other.
Л(
18:52
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Read-only transactions, sure ?

Please attach with gdb and make stack backtrace.
w
19:11
walter
In reply to this message
please ignore this result.

I find 2 process use diff mdbx version. I will test more and feed back
Л(
20:15
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Nonetheless, it is unexpected behaviour.

Libmdbx checks signature, both of DXB and LCK files.
Thus if versions are incompatible your should get an error, but not a deadlock etc.
w
20:17
walter
I find the case very odd. If I use scripts download tar file and unzip, it will block. If I copy from build file, it will work. I try compare the file diff from download binary
20:31
In reply to this message
I find the problem.

If I use a musl static binary in process A, and glibc in process B. It will block.

I must open env with glibc with all process.
Л(
22:54
Леонид Юрьев (Leonid Yuriev)
In reply to this message
AFAIK, even should works.

On Linux libmdbx uses PTHREAD_PROCESS_SHARED+PTHREAD_MUTEX_ROBUST POSIX-2008 mutexes.
Regardless to musl or glibc, ones are backed/rely on Linux' futexes.

Advisory file locks (fcntl + F_SETLK/F_OFD_SETLK) should works too.

So, I would like to see a stack backtrace of both/all deadlocked processes.
22:56
This will help us understand exactly what is going on, maybe we will identify some problems or errors.
14 August 2024
EL
07:24
Eddy Lee
In reply to this message
I remember encountering this deadlock as well.
I ran an mdbx in musl container, and a glibc container, pointing to the same database, this. caused a no error deadlock on opening the db.

it used to happen a lot when the erigon docker container was musl and everyone built locally on glibc - ppl would regularly report this problem trying to run erigon with different components, or have issues running glibc integration binary with a running erigon in musl contaiber. he solution was always to make sure your erigon components is all compiled under musl and glibc.

I also remember looking into it briefly (it didn't make sense), and if memory serves, there are incompatibilities between glibc and musls implementation of advisory locks, and I think it is a glibc quirk which breaks musl (unsurprisingly).
Л(
10:33
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Thanks for information, I will dig.
Л(
11:29
Леонид Юрьев (Leonid Yuriev)
Пользуясь положением снова напомню, что продаю излишки ОЗУ после унификации модулей.
https://www.avito.ru/moskva/tovary_dlya_kompyutera/operativnaya_pamyat_3470050498
20 August 2024
В
16:23
Виктор
Добрый день. На макбуке с m1 возникает ошибка при создании map_handle в режиме записи с ключом usual:
Assertion failed: (env->dbs_flags[dbi] & DB_VALID), function dbi_bind, file mdbx, line 282.
С другими ключами ошибки нет. На линуксе и на винде такой ошибки нет
Л(
17:07
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Приветствую.

1. Укажите на какой конкретно версии (git commit hash) у вас проблема.

2. Сделайте минимальный тест воспроизводящий проблему.
Это позволит понять в чем дело, и либо поправить libmdbx, либо указать на причину в вашем коде.
В
18:39
Виктор
Код воспроизведения:
mdbx::env::operate_parameters operateParameters(100,10);
mdbx::env_managed::create_parameters createParameters;
std::string path = "example.mdb";
mdbx::env_managed env(path, createParameters, operateParameters);
mdbx::txn_managed txn = env.start_write(false);
mdbx::map_handle testHandle = txn.create_map("fap1", mdbx::key_mode::usual, mdbx::value_mode::single);
18:41
Воспроизводится на последних версиях master и stable ветках
Л(
18:43
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Принято.

Ближе к ночи по мск буду смотреть.
Л(
22:43
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Ваш код/сценарий очень прост и какой-либо проблемы не воспроизводит.
"Аблочного" оборудования у меня нет, но даже если-бы было, то крайне маловероятно что именно этот код воспроизводит проблему — просто потому, что это самый простой сценарий и подобные случаи проверяются тестами.

Тем не менее, вполне возможно что некая проблема есть, и можно попробовать её устранить.
Исторически API связанный с dbi-хендлами унаследован от LMDB и BDB.
Основной недостаток этого API в том, что dbi-хендлы "выплескиваются" за рамки ACID-изоляции внутри процесса.
Например:
1. Если dbi-хендл открывается в read-only транзаеции, то он становится сразу доступным во всех транзакциях.
2. Если dbi-хендл закрывается или связанная с ним таблица удаляется, то это происходит также во всех транзакциях.

Постепенно я пытаюсь улучшить ситуацию, устранить неожиданности и хрупкие места, и т.п.
Но бывают как регрессы, так и какие-то упущения в логике.
Видимо вы что-то нашли.
Но чтобы понять в чем дело нужен полный сценарий, как минимум воспроизводящий предыдущую активность с dbi-хендлов и многопоточность.
21 August 2024
В
07:40
Виктор
Удалось локализовать проблему. Действительно, ситуация оказалась сложнее. Из-за ошибки в логике первоначально хендел был создана не с тем ключом. Код воспроизводящий ошибку в файле. Но в связи с этим два момента:
1. Было бы неплохо сообщать причину по которой невозможно создать/открыть хендл
2. Странно что на винде можно открывать хендл на запись с разными ключами и делать записи
Л(
11:21
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Принято, но посмотреть смогу только ближе к ночи по мск.
Л(
11:39
Леонид Юрьев (Leonid Yuriev)
In reply to this message
1. Таких неплохо-бы очень много, даже есть в TODO/FIXME.
Соответственно, постепенно что-то допеределывается, но будет неправильно тратить много времени (оплаченного PT - Positive Technologies) на доработки, которые не требуются для PT.
Если хотите быстрее, то присоединяйтесь к разработке, либо спонсируйте.

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

В вашем случае, предполагаю, что дело в несольких тредах, которые работают с dbi-хедлами не рационально.
Сейчас в MDBX cознательно оставлена некоторая "хрупкость" ради lockfree на основном (fastpath) пути работы с dbi-хендлами.
Смысл такого решения в том, что:
- полностью устранить хрупкость при конкурентном открытии/закрытии хендлом (и другими подобными ситуациями) можно только внедрив подсчет ссылок и/или существенную отложенную RCU-подобную обработку, либо очень дорогие блокировки.
- это уронит производительность и усложнит код, но не сделает работу приложений стабильной при нерациональных конкурентных действия с хендлами (всё равно будут спорадически ошибки MDBX_BAD_DBI и т.п.).
22 August 2024
Л(
00:28
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Исправление пролито в ветку devel на Gitflic.
Проверяйте.
Если всё будет хорошо, то завтра/послезавтра портирую исправление в stable-ветку, а также продвину master.
В
09:05
Виктор
На маке генерится исключение exception of type mdbx::incompatible_operation: MDBX_INCOMPATIBLE, что логично, на винде исключения нет
Л(
09:14
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Возврат ошибок и генерация исключений не зависит от платформы.

Скорее всего на маке у вас таблица не пустая, а на винде пустая — поэтому там она пересоздается (ибо передаётся MDBX_CREATE), вместо возврата ошибки.
В
10:03
Виктор
Действительно ошибка на маке идет после второго запуска. Когда таблица уже создана. Но на винде ошибок нет даже при созданной таблице
Л(
10:05
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Еще раз обращаю внимание:
- при передаче флажка MDBX_CREATE пустая таблица успешно пересоздаётся с новыми флагами/опциями.
- если же таблица не пустая, либо MDBX_CREATE не указан, то будет возвращена ошибка.
Л(
23:15
Леонид Юрьев (Leonid Yuriev)
In reply to this message
На stable-ветке не воспроизводится.
Ваш тест отрабатывает, а в коде нет подобной assert-проверки (с контролем флага DB_VALID или чего-либо похожего).
30 August 2024
Л(
00:18
Леонид Юрьев (Leonid Yuriev)
Прошла "охлаждающая" неделя без каких-либо коммитов и/или сообщений/жалоб.

Тэг v0.13.1 проставлен, выпуск новой версии состоялся.
Оформлением выпуска займусь в выходные.
👍
w
n
07:26
nicm
Regarding long-lived read-only transactions: When are "pages freed" (by newer read-write transactions)?
07:27
Is this when replacing existing value with new value (for the same key) and deleting a key? Only those 2 cases?
Л(
12:24
Леонид Юрьев (Leonid Yuriev)
In reply to this message
In general, no.

I don't want to answer in more detail. It is better to watch a presentation/video about the internal structure and operation of MDBX and/or LMDB.
👌
n
31 August 2024
routingtable invited routingtable
5 September 2024
DH
11:53
Dvir H
In a transaction, I want to write many small objects (less than one page) and a few large ones (a few pages). I am okay with that the large objects will be written to the end of the file and will increase the DB size if this helps to avoid long searches in the free list. What is the best way to achieve this?
Will configuring MDBX_opt_rp_augment_limit to 1 achieve that?
Л(
14:50
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Basically, yes.
However, this is exactly what you should not do, because if the threshold is too small, the GC search will actually be disabled.

I strongly recommend using MDBX_opt_gc_time_limit instead of MDBX_opt_rp_augment_limit.
You can set the search duration limit to a convenient/desired value, for example, 65 (which corresponds to 1 millisecond, since 65535/1000=65).
DH
16:24
Dvir H
In reply to this message
By being disabled, do you mean that sometimes (or each time) a page is needed, a search in the free list will not be conducted, and a page will be added to the end of the file? If so, what is the smallest number of MDBX_opt_rp_augment_limit that guarantees that each time a page is needed, a search in the free list will be done before expanding the file?
Regarding the MDBX_opt_gc_time_limit suggestion, I see that this value will affect only if the MDBX_opt_rp_augment_limit limit has come, so I still need to configure MDBX_opt_rp_augment_limit with a small value.
Л(
20:54
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Да, вы правы; а я немного запутался, так как запомнил поведение которое было вое время разработки, но не то что было реализовано в итоге (более гибкий вариант с сохранением обратной совместимости).

Yes, you're right; and I got a little confused, because I remembered the behavior that was during development, but not what was finally implemented (a more flexible option while maintaining backward compatibility).

--

Минимального "хорошего" значения, о котором вы спрашиваете, для MDBX_opt_rp_augment_limit нет — это целиком определяется сценарием использования, в том числе содержимым GC (что в общем случае зависит от всей истории операций с БД). Чтобы БД не увеличивалась зря нужно не ограничивать поиск вглубь GC, а все ограничения — это уже компромиссы.

There is no minimum "good" value,that you are asking, for MDBX_opt_rp_augment_limit — this is entirely determined by the usage scenario, including the contents of the GC (which generally depends on the entire history of database operations). In order for the database not to increase in vain, it is necessary not to limit the GC search, and all restrictions are compromises.
6 September 2024
DH
01:13
Dvir H
In reply to this message
I am asking for a good value for MDBX_opt_rp_augment_limit only in the case where big objects need to be written to the end of the file without spending time searching in the free list, but small objects (and branch pages) need to be taken from the free list.
01:13
From your first answer, I understand (maybe incorrectly) that configuring MDBX_opt_rp_augment_limit to one in some cases will cause an increase in the DB also when putting small values (and when there are still pages in the free list).
Л(
08:49
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Single-page allocations do not require a search GC, as anyone (the first one that comes) is suitable page.
And consecutive pages required only for big objects when MDBX_ENABLE_BIGFOOT=1 (enabled by default for 64-bit arches).

So MDBX_opt_rp_augment_limit effects only big objects, and/or huge transactions if "big foot" feature disabled (i.e. MDBX_ENABLE_BIGFOOT=0).

However, searching deep into GC (when it happens) has another positive effect -- lists of pages from GC are loaded into memory and sorted. Thus, after such a search, the GC "straightens up", the pages adjacent to the end of the allocated space are deallocated, and subsequent page allocations occur closer to the beginning of the file.
Next version will have an API to enforce such GC "straightens up" with controllable options/thresholds -- this allow explicit defragmentation, etc.
👍
DH
7 September 2024
Л(
09:26
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Оформлен выпуск libmdbx 0.13.1.

- Теперь лицензия Apache 2.0 (вам требуется добавить NOTICE в ваши проекты и т.п.);
- 157 files changed, 41949 insertions(+), 33741 deletions(-);
- Очень много нового и доработок.

https://gitflic.ru/project/erthink/libmdbx/release/825a0564-1741-4332-a843-f17585311271
👍
N
LP
8 September 2024
В
08:32
Виктор
Добрый день. В ветке devel отсутствует реализация функциий upper_bound и upper_bound_multivalue
Л(
09:51
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Принято. Посмотрю как будет время.
Л(
11:49
Леонид Юрьев (Leonid Yuriev)
In reply to this message
👍
В
9 September 2024
Аntоn invited Аntоn
Александр invited Александр
Sergey Ryabinin invited Sergey Ryabinin
11 September 2024
П
13:40
Павел
Добрый день!
Подскажите, подалуйста, существует ли атомарная операуия над базой - взять/положить? Т.е. за одну операцию получить по ключу предыдущее значение и перезаписать на новое.
СМ
13:48
Сергей Мирянов
mdbx_replace
13:49
ну и если вы делаете в одной транзакции - то можете делать и раздельными операциями
П
14:11
Павел
Спасибо!
В
14:54
Виктор
Добрый день. Под виндой при одновременной работе в нескольких потоках возникает ошибка при создании mdbx::env_managed:
C++ exception, flags=0x81 (first chance) in mdbx!mdbx::db_unable_extend::~db_unable_extend
abort() has been called.
Eugene | Silver Koi (WILL NEVER DM FIRST) invited Eugene | Silver Koi (WILL NEVER DM FIRST)
12 September 2024
Л(
01:34
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Windows не умеет изменять размер отображаемой секции.
Поэтому в libmdbx много костылей, предохранителей, обхоных маневров и т.п.

В частности, если размер БД действительно необходим менять, то для этого может потребоваться сделать umap, изменить размер и затем обратно отобразить в память.
Соответственно, перед unmap приостанавливаются работающие с БД потоки, но это не всегда возможно (в особенности если используется MDBX_NOSTICKYTHREADS).

Однако, если ошибка возникает при открытии БД, то скорее всего это недочет в логике.
Давайте testcase, в выходные посмотрю и постараюсь поправить.
👍
w
В
11:09
Виктор
Ошибка возникает при открытии. Тест прилагаю
Л(
15:18
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Принято. Вечером буду смотреть, если электрики не подведут.
13 September 2024
Л(
10:26
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Ваш тест некорректный:

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

2. Для физически одной БД у вас три экземпляра env в одном процессе.
Это не будет работать без MDBX_DBG_LEGACY_MULTIOPEN, но этот режим не нужно использовать без явной необходимости.
Поэтому вас тест падает по другой причини, а именно по "terminate called after throwing an instance of 'std::system_erro (Resource temporarily unavailable" (на Windows код ошибки может быть другим).
Л(
11:36
Леонид Юрьев (Leonid Yuriev)
In reply to this message
На всякий случай я проверил отсутствие логической ошибки (о которой подозревал и беспокоился) в базовом/простейшем сценарии.
Ошибки нет, как минимум я её не вижу.
Но проверял я пока только под Linux, ибо Windows у меня пока нет (ноутбук с нативной виндой в сервисе, а ставить в виртуалку для отладки сейчас некогда).

Если кратко, что должно работать и когда может возникать ошибка "невозможно расширить БД":
- должно отрабатывать изменение размера на стадии открытия БД (для этого нужно задать геометрию в create_parameters), см. как пример).
- должно отрабатывать увеличение файла БД без изменения верхнего предела размера, в том числе автоматическое приращение в рамках установленной ранее геометрии.
- на Windows из-за системных ограничений может не работать (возвращать ошибку) при изменении геометрии, причем как с увеличением, так и тем более с уменьшением размера отображения в ОЗУ.

Выход достаточно простой: задавайте необходимую геометрию (хотя-ы минимальный и максимальный размер) до открытия БД и после не меняйте его "на ходу".

Если из обозначенного выше что-то не будет работать, то давайте testcase.
Но только будьте внимательнее и тщательнее подходите как к сценарию, так и к вопросам синхронизации/взаимодействия тредов (желательно не делать из меня тестировщика ваших тестов)..
В
15:26
Виктор
In reply to this message
Действительно, когда убрали создание env в потоках, то ошибка ушла. Но тогда возникает вопрос: возможно ли сделать так чтобы в одном просессе было много потоков в которых асинхронно производились операции записи/чтения, причем количество читающих операций было большинство?
Л(
16:24
Леонид Юрьев (Leonid Yuriev)
In reply to this message
1. Что-то моя картина не совпадает с тем, что вы описываете.
Если у вас было несколько экземпляров env для одной БД, то ошибка должна быть другая (не db_unable_extend).
Возможно это еще какая-то особенная ситуация на Windows, либо недочет в коде.
Нужно разобраться с этим.

2. MDBX подерживает не более одной активной пишущей транзакции для всех работающих с БД процессов.
Это концепция (single writer approach) фундаментальна, позволяет избавиться от всех прочих блокировок и массы накладных расходов.
Если вам требуется одновременно более одной пишущей транзакции, то libmdbx вам точно не подходит.

3. Читающих транзакций может быть много и все они могут выполняться одновременно и преимущественно без блокировок (aka lockfrеe).
Блокировки требуются при регистрации в глобальной таблице читателей и синхронизации mmap/unmmap при изменении размера БД.
Кроме этого, долгие читающие транзакции мешают переработке старых MVCC-снимков (ибо собственно читают их).

Очень рекомендую RTFM.
В
20:45
Виктор
В нашей ситуации можем всех писателей поставить в очередь и тогда будет только одна пишущая транзакция. Что касается RTFM то постоянно штудируем мануал по libmdbx, а также исходный код. Будем очень благодарны, если дадите ссылки на дополнительные материалы.
Л(
20:52
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Вся информация (вроде-бы) есть в документации.
Сама документация — это doxygen-комментарии и несколько md-файлов в /docs.
Из всего этого автоматически генерируется содержимое https://libmdbx.dqdkfa.ru/

Рекомендую прочитать всё два раза в таком порядке:
- сначала раздел "Getting started"
- затем последовательно весь mdbx.h
- потом глянуть на классы в mdbx.h++
В
21:30
Виктор
Именно так и изучаем ). Но по поводу env возникло недопонимание, оказывается в одном процессе должен быть только один экземпляр env и его нельзя создавать в потоках. Спасибо за подсказку. Самое интересное то что в июльской версии ошибка не проявлялась и все прекрасно работало на винде, линуксе и маке.
Louis invited Louis
L
23:42
Louis
всем привет!

Thanks for building and opensourcing such a great database library 🙂

I'm building a project where I need to store a large amount of byte strings (20 bytes ethereum addresses) in an "ordered set" (each value has a position in the set). I want to lookup string but also find a string by its position.
What I did so far is storing all the strings in a simple flat file that I can easily lookup by offset, and I'm using libmdbx as an index of this file, i.e to find the position of a given string.
It's working quite good with a large number of entries (~290M) but I'm wondering if I could optimise it further:
- Would a libmdbx table be faster to lookup than the simple file offset?
- I'm using MDBX_NOOVERWRITE to be 200% sure that I don't store a string twice, even though I already know they are unique at this time. Is it slower than MDBX_UPSERT?
- Would MDBX_APPEND speed up the insertion (keys are random-like and inserted in batches)
- Would you recommend a better layout of the tables?

Thanks for your help!
👍
VS
14 September 2024
Leo invited Leo
Л(
10:42
Леонид Юрьев (Leonid Yuriev)
In reply to this message
> Would a libmdbx table be faster to lookup than the simple file offset?

> Would you recommend a better layout of the tables?

Historically and for now, MDBX supports only ordered key-value sets/maps, optionally with multi-values (aka dupicates for keys) and/or fixed-length 32/64-bit keys (aka integers keys).
So for your case (20-bytes keys and 64-bit values) likely there no some faster than usual key-value table.

However, I think you should try one trick:
- use MDBX_INTEGERKEY + MDBX_DUPSORT;
- use only 32-bits of full keys, i.e. first 4 bytes or a hash (yes, you get collisions/duplicates);
- the rest of keys store as a prefix of values;
- for lookup a key first search the "bucket" by first 4-bytes, and then by the remainder of the key among the values (through collisions/duplicates).

Thus, the space/volume of a keys-part in a b-tree is significantly reduced, which could dramatically speed up the search, etc.
It is also worth trying with 64-bit keys instead of 32-bit ones — this will give fewer collisions and may be integrally a little faster (or slower, can not say in advance).

> I'm using MDBX_NOOVERWRITE to be 200% sure that I don't store a string twice, even though I already know they are unique at this time. Is it slower than MDBX_UPSERT?

This does not affect performance, since internally a position search is performed the same way in both cases.
> Would MDBX_APPEND speed up the insertion (keys are random-like and inserted in batches)

Basically MDBX_APPEND affect only page splitting during insertion.
Without MDBX_APPEND an each filled page split in the middle, which gives half-filled pages when insertions performed in keys-order.
With MDBX_APPEND a filled pages splits at the "right" edge, which gives full-filled pages when insertions performed in keys-order.

So the MDBX_APPEND could speedup insertions on keys-order, since 2x-times reduces the number of a pages added to b-tree.
On the other hand, subsequent inserts in random order will be slower, since most of a pages are 100% full and they need to be split for each insertion.
👍
w
L
L
18:25
Louis
In reply to this message
Thank you for the detailed explanations!

I like the idea of using integer keys and dupsort. I actually « only » have ~300M keys currently and it’s not growing very fast. With 32 bit keys there’s a 7% chance of collision so the search can still be fast.
🤝
Л(
15 September 2024
DH
10:14
Dvir H
In reply to this message
Because the key and value size are fixed, you can use MDBX_DUPFIXED, saving you 10 bytes per entry.
👍
Л(
Л(
10:54
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Just for clarify:

This is true for "LEAF 2 pages" which created inside nested b-tree for a set of (multi)values of particular key, but never for a single-value cases.
Since only the 7% collisions are expected (i.e. a multi-value entries) then the savings will be negligible, but still there will be.
П
12:26
Павел
Добрый день!
Собрал библиотеку для ос Linux и Windows.
Проверил в приложении - все работает отлично.
Но вот вопрос: почему для Windows собранная dll весит в районе 500кб, тогда как для Linux so получился более чем 3мб?
Для Linux использовал GNU, для Windows делал в Visual Studio 2022.
Л(
12:29
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Для не-Windows см. man strip.
П
12:30
Павел
Спасибо!
b
12:59
basiliscos
mdbx:14986: override_meta: Assertion `(!env->me_txn && !env->me_txn0) || (env->me_stuck_meta == (int)target && (env->me_flags & (MDBX_EXCLUSIVE | MDBX_RDONLY)) == MDBX_EXCLUSIVE)' failed.


Первый раз выстрелил ассерт v0.13.0
13:00
если что есть core-dump с debug info
Л(
13:03
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Насколько понимаю это уже исправлено коммитом 69df6e6a.
Поэтому просто переходите на последний релиз или текущий master.
👍
b
b
13:03
basiliscos
Спасибо
Л(
13:06
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Не за что.

На всякий — ошибка была в условии assert-а, а не в основной логике.
👍
b
16 September 2024
AV
08:43
Artem Vorotnikov
переношу растовые биндинги на 0.13.1

в тесте вылезло
mdbx:17067: cursor_ops: Assertion `is_filled(mc) && !inner_filled(mc)' failed.
Л(
09:20
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Скорее всего это последствия неверного использования API, т.е. обращения к курсору в плохом/недо-позиционированном состоянии.
Но теоретически должна возвращаться ошибка до наскока на assert.

Короче, давай какой-нибудь testcase. Конечно, желательно максимально простой и максимально изолированный.
Смотреть буду вечером, если 390XP (Husqvarna) не покусает меня ;)
AV
17:53
Artem Vorotnikov
In reply to this message
    let dir = tempdir().unwrap();
let db = Database::open(&dir).unwrap();

let txn = db.begin_rw_txn().unwrap();
let table = txn
.create_table(None, TableFlags::DUP_SORT | TableFlags::DUP_FIXED)
.unwrap();
for (k, v) in [
(b"key1", b"val1"),
(b"key1", b"val2"),
(b"key1", b"val3"),
(b"key2", b"val1"),
(b"key2", b"val2"),
(b"key2", b"val3"),
] {
txn.put(&table, k, v, WriteFlags::empty()).unwrap();
}

let mut cursor = txn.cursor(&table).unwrap();
assert_eq!(cursor.first().unwrap(), Some((*b"key1", *b"val1")));
assert_eq!(cursor.get_multiple().unwrap(), Some(*b"val1val2val3"));
assert_eq!(cursor.next_multiple::<(), ()>().unwrap(), None);
Л(
22:54
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Принято.

На первый взгляд проблема в условии внутри assert-а, там лишнее отрицание (не нужный !).
Завтра еще раз посмотрю и потестирую.
Пока просто удали/закомментируй этот assert у себя, если мешает.
18 September 2024
Unite invited Unite
Audacious Tux invited Audacious Tux
Л(
21:47
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Исправлено в master-ветке.
Тестовый сценарий я сделал на основе твоего примера, пока это только минималистская проверка в extra/dupfix_multiple.c++
🤝
w
КА
AV
22 September 2024
П
22:59
Павел
Доброго времени!
Есть большое количество данных, разбитых на группы, в каждой группе ключи с одинаковым префиксом.
Интересно, как база хранит такие ключи?
Дублируются ли префиксы для каждого ключа или же используются подобия префиксных деревьев с более компактным хранением?
☭k
23:10
☭ ktrace
хм. собрал 0.13.1. а reopenldap с ней же не соберётся?
24 September 2024
Deleted invited Deleted Account
w
12:32
walter
In reply to this message
I want the disk tree key and value always memory aligned, so when I read a non-copy result I can read the number value directlly. Key always is 64bit number, value is a struct with memory aligned fields.


Is there a options to make mdbx work like this?

And thanks for the great work
Л(
13:43
Леонид Юрьев (Leonid Yuriev)
Болею, пока нет сил отвечать
w
13:43
walter
take care your self. no hurry
AS
14:49
Alex Sharov
In reply to this message
See Integer_Keys flag of DBI
👍
Л(
Red ( tornadowithdraw.com ) invited Red ( tornadowithdraw.com )
25 September 2024
w
12:34
walter
In reply to this message
Thanks for tips. I use Integer_Keys for my app, I am not sure the key always aliged . (I guess so, since it compared inside mdbx).

more importand is the value also need aligned, since value can be huge and complicated. avoid copy into aligned memory block will be huge win.
Л(
14:37
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Увы, нет.
Исторически в MDBX нет ни префиксных деревьев (aka Trie), ни сжатия префиксов ключей (aka index prefix compression).

Причина в совокупности трех факторов:
- исходно подобное не было реализовано ни Мартин Хеденфок, ни Говарда Чу, не реализовали это в исходном коде;
- реализация любого подобного механизма сравнима с переписыванием движка;
- мне (а также Positive Technologies, и ранее Петер-Сервису/МегаФону) это не требовалось настолько, чтобы оправдать разработку.

В MithrilDB планировалось реализовать параметризуемый вариант префиксного дерева на основе нарезки ключей на 8-байтные куски.
Проще говоря, планировалось строить всё вокруг префиксного дерева для выровненных фрагментов ключей размером в машинное слово.
Возможно это когда-нибудь будет реализовано и опубликовано/открыток, но каких-либо обещаний я не даю.
П
14:40
Павел
In reply to this message
Спасибо за пояснения!
Л(
14:53
Леонид Юрьев (Leonid Yuriev)
In reply to this message
The basic concept of alignment of keys and values in MDBX is as follows:
- the pages are filled by key-value pairs from the bottom up, i.e. from the end of page;
- leaf-pages contains keys with values, branch-pages contains only keys;
- thus if the length keys and values will be multiple of N-bytes, then you will get N-bytes alignment.
14:55
In reply to this message
Спасибо. Выздоравливаю.
Предположительно новый штамм/вариант COVID.
Двое суток 40℃, но уже намного лучше.
w
14:56
walter
drink enough watler and get better soon.

let me confirm this again:

If the key is int64, and size alway is 8. then it always aliged to 8byte.

if the value size alway is 8*x size, then thery are alwasy aligned to 8 byte without memory copy?
Л(
16:35
Леонид Юрьев (Leonid Yuriev)
In reply to this message
For leaf-pages all keys and all values both must be N-bytes length to be N-bytes aligned.
For branch-pages only all keys must be N-bytes length to be N-bytes aligned.
w
16:50
walter
Thanks for explain.

I will do more test with this case:

>>> For leaf-pages all keys and all values both must be N-bytes length to be N-bytes aligned.

key = always 8 byte

value = always 8 * x byte. ( x >=1, x <= 2048)

if every key and value like this, I guess I will be safe.
👍
Л(
26 September 2024
Л(
07:04
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Наверное не соберется, но это легко поправить.
П
08:20
Павел
Добрый день! Подскажите, пожалуйста, а если в базе на один ключ несколько значений, дублируется ли ключ для каждого значения или хранится в одном экземпляре?
Л(
08:24
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Ключ хранится один.

Советую полностью прочитать документацию, лучше два-три раза.
🙏
П
n
10:28
nicm
Any ideas why mdb_put could be leaking memory (in the way I use it)? I just temporarily malloc memory (for key and value), put data in there, and put those into mdb_put. I also tried just creating the MDB_vals on the stack and using those, but still leaks.

Is mdb_put doing some extra alloc under the hood or something? Can you think of anything? Thanks.
10:29
I clearly make mistakes someplace. However I do see db size on disk increasing, so my write txns are getting committed.
AS
10:29
Alex Sharov
In reply to this message
Likely you don’t commit/rollback transaction.
n
10:30
nicm
In reply to this message
will db size on disk increase, even without commits?
AS
10:30
Alex Sharov
In reply to this message
Likely in some case (like when error happens) you don’t rollback read txn
10:31
In reply to this message
Long-living readers do not allow re-use free pages and db can grow
n
10:31
nicm
I will think about this more, thanks for ideas.
Л(
10:42
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Why did you think there was a memory leak?

MDBX internally use lazy-release buffers for page shadowing and such a cache is released during the env closing.
Has memory been released up after mdbx_env_close() ?

You can also use ASAN/Valgrind, etc. to track the origin(s) of memory allocation(s).
😁
w
КА
КА
10:43
Кемаль Ататюрк
In reply to this message
did you tried running program under valgrind ?
n
14:09
nicm
In reply to this message
Thanks, I just tried periodic env close/reopen. Even then memory keeps growing.

(By "memory growing", I mean the RSan part in "top" keeps growing (to gigabytes) which is why I guessed it was some leak. (I already know it's normal (afaik) for the Rsfd part in "top" to grow because that's just memory mapping stuff and not what most people think of as "actual RAM usage".))
🤝
Л(
n
19:10
nicm
Here are more details I noticed that might help out:
19:16
When running the program (it's a Haskell program btw) normally, the "leak" (massive RSan increases) appears.

However when running the same program in memory profiling mode (after recompiling it with profiling support), the "leak" (massive RSan increase) goes away.

Both ways, the program gives the same result so it's for sure doing the same thing in both ways.
19:16
Can we conclude anything interesting/useful from this?
19:23
🎉
КА
19:24
So for some reason, my normally compiled program is doing some more quadrant 1 (RSan) stuff.

But any ideas why? Interesting.
n
19:50
nicm
(Sorry if I waste your time. Maybe this is just a Haskell issue and little/nothing to do with lmdb/mdbx.)
Л(
19:51
Леонид Юрьев (Leonid Yuriev)
In reply to this message
It is 99.99% probability you have the problems with Haskell' bindings for libmdbx, but not with libmdbx itself.

I'm not an expert in Haskell, but AFAIK lazy computation model includes lazy release/destroy of an resource(s).
So when you compile/run code in memory/resource profiling mode an resources released/destroyed explicitly eagerly.

Then you should get an answer like "How to call/execute external destructor(s) eagerly from Haskell?" and fix bindings and/or your code.

Two other points might helps:
1. Since libmdbx is C/C++ library you can still use Valgrind/ASAN with appropriate options to check all glibc allocations, track origins of allocations in the code, etc.
2. You can refactor/rewrite bindings to use libmdbx.so via dlopen() + dlsym(), and then dlclose(). This will way will release all glibc' objects (maps, allocation, etc).
🤝
n
Л(
20:10
Леонид Юрьев (Leonid Yuriev)
In reply to this message
It is also worth explaining that in most cases Haskell is legitime not to call deferred destructors for objects on the stack and in the heap at program termination, since all process resources will still be released when it is completed/destroyed.

However, this may be incorrect for destructors of external objects (outside Haskell) and fundamentally incorrect for destructors with side effects.
Thus, such types/classes/destructors should be explicitly marked (with attributes, etc.) to prohibit deferred execution and/or prohibit cancellation at program termination.
🤝
n
27 September 2024
U
13:32
Unite
is it okay to have a long read transaction (hours, not seconds) when the db already has a write transaction open that doesn't write any data (no puts, no commits)? will it blow up my db size and exhaust space?
AS
19:39
Alex Sharov
In reply to this message
No.
🙏
U
Л(
19:41
Леонид Юрьев (Leonid Yuriev)
In reply to this message
Wrong answer.

+Hm, nonetheless, it should be clarified.
19:47
In reply to this message
An extra-long read transaction(s) become a trouble when more than one write transaction committed.

Briefly, when a second (or more) write transaction committed after a read transaction started, then such read transaction block a read'ed MVCC snapshot (and any subsequent snapshot) to be reclaimed.

So, no any problems if a 1-10 small transactions committed after a reader started.
Contrary, a DB will blow up if a 10K small (or a 10 huge, etc) transactions will be committed.
🙏
U