Новая версия KVX 6.4

KVX — это слой абстракции состоящий из двух частей: базового АПИ и АПИ управления стримами, тейк, дроп, фолд, катаморфизм, как вы любите. Основное АПИ в модуле kvx, а стримовое в kvx_stream. Пользуясь случаем хочу пропиарить похожие решения в области хранения данных на эрланге: datum и серия библиотек от Дмитрия Колесникова, replayq от Фенга Ли, а также немногих других, кто пытался абстрактно решить проблему.

Why KVX?

Предназначение KVX:

— предоставление интерфейса абстрагирования широкого спектра хранилищ;
— предоставление удобного Эрланг REPL интерфейса для работы с рекордами;
— разделение на базовый (get put) и стрим интерфейс итераторов (next prev);
— набор драйверов (внутренняя база, внешняя база и файловая система);
— слои данных: файловая система, цепочки сообщений, банковские транзакции, деревья подписей, трейсы бизнес-процессов, блокчейны, системы очередей, тайм сириес, вот это всё.

Начиная с легковесной модификации KVX сменила название, теперь это "Абстрактная База Цепочек", а не "Абстрактная База Термов", так как выделелось отдельное стрим АПИ.

synrc/kvx — 6.4

Расскажем немного про теоретические основы KVX. Эта библиотека позволяет или должна позволять хранить и извлекать любого вида структуры предоставляя семантику управления курсорами next prev, которой обладают деревья. Поэтому можно сказать что это интерфейс оператора к цепочным и древовидным хранилищам. В своей основе KVX поддерживат три механизма хранения цепочек:

1) первый способ, двусвязные списки, явный, где next и prev указатели непосредственным образом присутствуют в данных, подходит даже для управления деревьями;

2) второй, явный, где есть только next, этот способ подходит для списков, много людей спрашивают про этот способ, но мы его не использовали никогда и имплементации под рукой нет;

3) и третий, не явный способ, где записи встраиваются в существующий индекс, т.е. btree таблицу, zero-overhead.

В каком-то смысле первый и второй способы реализую определённый слой поверх KVX, так как kvx_stream работает с любыми стораджами полиморфно, а вот kvx_st драйвер стримов сделан специально для rocksdb.

Деревья

Двусвязные списки или деревья, первая модель, которая ложится прямо из С++ классов, при изучении программирование, это хранить в базе прямые указатели, такая система хранения может даже использоваться если integer поменять на pid, тогда можно будет точечно восстанавливать историю вызова.

-record(iter, { id = [] :: [] | integer(), next = [] :: [] | integer(), prev = [] :: [] | integer() } ).

Первое поле любой таблицы — это ее имя, второе — id, третье и четвертое next и prev (сигнатура бинарного ветвления). Полиморфно по этим смещениям мы ожидаем наличие этих полей для линковки данных в цепочки. Базовая часть вместе называется #iter.

kvx_stream — #iter{}

За полиморфное явное управление application level полями next prev рекорда #iter отвечает модуль kvx_stream. Например файловая система не предоставляет управление своими курсорами, поэтому имплементация fs требует линковочных полей.

Списки

Односвязные списки или просто списки, вторая модель. Всё тоже самое, но без возврата назад. Не поддерживается пока ни одной версией KVX.

-record(ite, { id = [] :: [] | integer(), next = [] :: [] | integer() } ).

kvx_stre — #ite{}

Этот модуль не имплементирован и рекорд не специфицирован.

Встраивание в индекс

Третья модель представляет собой прямой, более эффективный способ встраивания пространства бизнес-объектов в пространство ключей нижлежащей таблицы, и передача управления курсорами BTree таблицы сразу в драйвер, а не в ручной link walking как в было в riak. Базовая часть полиморфных рекордов состоит только из имени таблицы и id и имеет название #it. Эта модель используется в rocksdb бекенде, который появился в 6.4.

-record(it, { id = [] :: [] | integer() } ).

Таким образом, мы можем эмулировать таблицы, в рамках единого пространства ключей, добавляя их как префиксы к ключу (little endian), который будет автоматически отражаться в основном BTree дереве, но также в этом пространстве вы можете хранить и фиды, т.е. стримы, например топики /p2p/maxim/doxtop. Это требует специальной модификации kvx_st для работы с рекордом #it, в отличии от двухсвязнного #iter который работает с драйвером kvx_stream.

kvx_st — #it{}

За управление курсорами по итератору базы данных отвечает модуль kvx_st.

На примере

Пример кода, который показывет основную идею на примере драйвера rocksdb:

1> {ok,Ref} = rocksdb:open("hey",[{create_if_missing,true}]).
2> rocksdb:put(Ref, <<"/users/1">>,<<"maxim">>,[{sync,true}]).
3> rocksdb:put(Ref, <<"/users/2">>,<<"doxtop">>,[{sync,true}]).
4> rocksdb:put(Ref, <<"/users/3">>,<<"vlad">>,[{sync,true}]).
5> rocksdb:put(Ref, <<"/staff/1">>,<<"vlad">>,[{sync,true}]).
6> rocksdb:put(Ref, <<"/staff/2">>,<<"maxim">>,[{sync,true}]).
7> rocksdb:put(Ref, <<"/staff/3">>,<<"doxtop">>,[{sync,true}]).
8> {ok,I} = rocksdb:iterator(Ref,[]).
9> rocksdb:iterator_move(I,{seek,<<"/staff/">>}).
10> rocksdb:iterator_move(I,next).
11> rocksdb:iterator_move(I,next).
12> rocksdb:iterator_move(I,next).
13> rocksdb:iterator_move(I,{seek,<<"/users/">>}).
14> rocksdb:iterator_move(I,next).
15> rocksdb:iterator_move(I,next).
16> rocksdb:iterator_move(I,next).

KVX REPL

Теперь, что бы сделать тоже самое на KVX, вы можете сделать просто:

1> kvx:ver().
{version,"KVX ROCKSDB"}
2> rr(kvx).
[emails,id_seq,it,iter,kvx,reader,schema,table,writer]
3> kvx:join().
ok
4> kvx:put(#emails{id=1,email="maxim"}).
5> kvx:put(#emails{id=2,email="doxtop"}).
6> kvx:put(#writer{id=2}).
7> kvx:put(#writer{id=1}).
8> kvx:all(writer).
[#writer{id = 1,count = 0,cache = [],args = [],first = []},
#writer{id = 2,count = 0,cache = [],args = [],first = []}]
9> kvx:all(emails).
[#emails{id = 1,next = [],prev = [],email = "maxim"},
#emails{id = 2,next = [],prev = [],email = "doxtop"}]
10> kvx:add(#writer{id=chain,args=#emails{email="[email protected]"}}).
11> kvx:add(#writer{id=chain,args=#emails{email="[email protected]"}}).
12> kvx:add(#writer{id=chain,args=#emails{email="[email protected]"}}).
13> kvx:all(chain).
[#emails{id = 1555244691729330000,next = [],prev = [], email = "[email protected]"},
#emails{id = 1555244699905648000,next = [],prev = [], email = "[email protected]"},
#emails{id = 1555244696660271000,next = [],prev = [], email = "[email protected]"}]

Единственный тест на стримы, который проходит:

> kvx:check().
ok

Документация

Надеюсь сделать эту рубрику в release notes официальной и регулярной, к сожалению, если не брать во внимание man страницы для модулей драйверов, то остается всего два модуля для которых нужна документация — это kvx и kvx_stream. Я конечно добавил страницы формально для всех модулей, в будущем хочется чтобы искусственный интеллект генерировал документацию. Еще я понял, что ченджлог и релиз ноутсы в своём блоге отличный способ авторского анонсирования! Сделал историю таких страниц в README.md.

В заключение

Репозиторий (который депендит медленно-компилирующийся rocksdb NIF драйвер от Бенуа Кисенау, Rust-версию Rocker от Максима Молчанова решил отложить, слишком часто раст обновляется, нельзя зафризиться). сразу включеет конфигурационный файл для rocksdb:

voxoz/kv

P.S. Встраивание в BTree можно применять не только в таких базах как rocksdb, но и для mnesia и для любых баз вообще.

P.P.S. Можно также использовать rocksdb как драйвер для мнезиии (написаный Ульфом Вигером для проекта Аэтернити), или скорее вид таблицы rocksdb_copies — mnesia_rocksdb.