Нова версія KVS 6.4

KVS — це шар абстракції, що складається з двох частин: базового АПІ та АПІ управління стримами, тейк, дроп, фолд, катаморфізм, як ви любите. Основне АПІ в модулі kvs, а стримове в kvs_stream. Користуючись нагодою, хочу пропіарити схожі рішення в області зберігання даних на ерланзі: datum та серії бібліотек від Дмитра Колесникова, replayq від Фенга Лі, а також небагатьох інших, хто намагався абстрактно вирішити проблему.

Why KVS?

Призначення KVS:

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

Починаючи з легковагої модифікації KVS змінила назву, тепер це "Абстрактна База Ланцюжків", а не "Абстрактна База Термів", тому що виділилося окреме стрим АПІ.

synrc/kvs — 6.4

Розкажемо трохи про теоретичні основи KVS. Ця бібліотека дозволяє чи повинна дозволяти зберігати та витягувати будь-якого виду структури, надаючи семантику управління курсорами next prev, якою володіють дерева. Тому можна сказати, що це інтерфейс оператора до ланцюжкових та деревоподібних сховищ. У своїй основі KVS підтримує три механізми зберігання ланцюжків:

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

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

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

В якомусь сенсі перший і другий способи реалізують певний шар поверх KVS, тому що kvs_stream працює з будь-якими сторожами поліморфно, а ось kvs_st драйвер стриму зроблено спеціально для rocksdb.

Дерева

Двозв'язні списки або дерева, перша модель, яка лягає прямо з класів С++, при вивченні програмування, це зберігати в основі прямі покажчики, така система зберігання може навіть використовуватися якщо integer поміняти на pid, можна буде точково відновлювати історію виклику.

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

Перше поле будь-якої таблиці – це її ім'я, друге – id, третє та четверте next та prev (сигнатура бінарного розгалуження). Поліморфно за цими зсувами ми очікуємо на наявність цих полів для лінкування даних у ланцюжку. Базова частина разом називається #iter.

kvs_stream — #iter{}

За явне поліморфне управління application level полями next prev рекорду #iter відповідає модуль kvs_stream. Наприклад, файлова система не надає управління своїми курсорами, тому імплементація fs вимагає лінкувальних полів.

Списки

Однозв'язні списки чи просто списки, друга модель. Все те саме, але без повернення назад. Не підтримується, поки що, жодною версією KVS.

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

kvs_stre — #ite{}

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

Вбудовування в індекс

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

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

Таким чином, ми можемо емулювати таблиці, в рамках єдиного простору ключів, додаючи їх як префікси до ключа (little endian), який буде автоматично відбиватися в основному BTree дереві, але також у цьому просторі ви можете зберігати і фіди, тобто стрими, наприклад, топіки /p2p/maxim/doxtop. Це вимагає спеціальної модифікації kvs_st для роботи з рекордом #it, на відміну від двозв'язкового #iter, який працює з драйвером kvs_stream.

kvs_st — #it{}

За управління курсорами по ітератору бази даних відповідає модуль kvs_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).

KVS REPL

Тепер, щоб зробити те саме на KVS, ви можете зробити просто:

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

Єдиний тест на стримі, який проходить:

> kvs:check(). ok

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

Сподіваюся зробити цю рубрику в release notes офіційною та регулярною, на жаль, якщо не брати до уваги man сторінки для модулів драйверів, то залишається всього два модулі для яких потрібна документація - це kvs и kvs_stream. Я, звичайно, додав сторінки формально для всіх модулів, але хочеться щоб в майбутньому штучний інтелект генерував документацію. Ще я зрозумів, що ченджлог та реліз ноутси у своєму блозі чудовий спосіб авторського анонсування!! Зробив історію таких сторінок у README.md.

На завершення

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

voxoz/kv

P.S. Вбудовування в BTree можна використовувати не тільки в таких базах як rocksdb, але й для mnesia і будь-яких баз взагалі.

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