10 подписчиков
Где-то три года назад я хотел построить свой лунапарк (пытался найти какой-нибудь референс на концепцию, пролистал до начала канала, не нашёл ничего конкретного, только сумбурный бред размазанный тонким слоем по постам). Рабочее название того лунапарка было Prism, сейчас переименовал его в Diffbelt, упростил концепцию и... за двое суток реализовал прототип.
... прототип правда ну совсем урезаный, работает только в in-memory виде, транзакционность только на уровне всей базы, но мой простенький тест проходит и слава Коту.
«я джва года ждал», суть такова:
◾️ Это база данных
◾️ key-value, иммутабельная, версионированная
◾️ База состоит из набора коллекций
◾️ Коллекции состоят из:
◾️ ◾️ Набора из записей вида {key, value, generationId}
◾️ ◾️ Идентификатора текущего и следующего поколения
◾️ ◾️ Набора reader'ов
◾️ Reader'ы это {readerId, generationId, collectionId}
◾️ Коллекции бывают двух видов:
◾️ ◾️ «Автоматические» — коллекции для добавления исходных данных, в них после запросов на мутации (добавление/изменение/удаление ключей) через какое-то время следующее поколение сменит текущее и будет запланировано следующее
◾️ ◾️ «Ручные» — производные коллекции, в которых можно&нужно управлять созданием поколений самостоятельно
🔻 В ручных коллекциях мы можем завести сколько нужно reader'ов указывающих на другие коллекции (будем называть их зависимостями), которые запомнят текущее поколение тех коллекций
◾️ Затем мы можем попросить ручную коллекцию начать новое поколение
◾️ Когда поколение начато, мы можем вычитать коллекции-зависимости целиком и записать в ручную коллекцию некоторый результат являющийся производным от содержимого коллекций-зависимостей
◾️ Когда закончили вычитывать зависимости и записали всё что хотели — можем закоммитить поколение, после чего чтения из этой коллекции будут отображать новые данные
🔸 После того, как в коллекциях-зависимостях произошли какие-то изменения, мы можем снова начать новое поколение
◾️ После чего просим дать нам diff'ы коллекций-зависимостей начиная с generationId сохранённого в соответствующем reader'е до актуального
◾️ Накатываем diff'ы на новое поколение
◾️ Коммитим поколение, но на этот раз говорим «а ещё вместе с коммитом обнови вот эти reader'ы на такие вот generationId»
◾️ Ну и теперь когда мы будем повторять с пункта 🔸, мы будем накатывать только те изменения, которых мы ещё не видели
◾️ Если в какой-то из моментов мы покрешились по каким-то причинам, то мы можем просто выкинуть начатое поколение и начать заново (мы знаем, на каком состоянии мы были до этого и никакие данные не затирались)
◾️ Если мы знаем что у нас вообще данные неконсистентны потому что у нас всю жизнь баг в коде сидел — можем радикально начать с пункта 🔻 и пересчитать все данные по-новой (удалив всё в новом поколении, а затем добавив правильно посчитанное)
◾️ Данные старых поколений удаляются тогда, когда на них не указывает ни один из reader'ов (этого я втираю, ещё не реализовал, но должно быть так)
Занудновато получилось, вот кейс который я в первом тесте написал:
◾️ Есть коллекция A в которой ключи это секунды, а значения это числа
◾️ Есть коллекция B в которой ключи это (секунды - (секунды % 60)), т.е. каждая минута, а значения — сумма чисел из коллекции A за такой интервал
◾️ У коллекции B есть reader коллекции A
◾️ Когда мы что-то добавляем в коллекцию A, через какое-то время это консистентно отображается на коллекцию B
◾️ Происходит это процессом, который наблюдает за изменениями идентификатора поколения A и когда видит, что он стал посвежее — стартует всю эту штуку с 🔸, смотрит на то какие числа были добавлены/изменены/удалены, вычисляет разницу за минуту и применяет изменения
◾️ Больше занудства: причём изменения он применяет поточно и идемпотентно, когда посчитал разницу из диффа для минутного диапазона он берёт значение из старой версии, меняет её и сохраняет в новую (т.е. тут допускается покрешиться где-то посередине чтения диффа, а затем повторить с момента который мы последним помним что точно корректно обработали)
Надеюсь более или менее понятно, что оно умеет.
#diffbelt
3 минуты
27 октября 2022