Eventsourcing + PostgreSQL = ♥

В грядущем выпуске Eventsourcing for Java 0.4 появится новый хорошо известный механизм хранения: PostgreSQL. До сих пор единственными другими параметрами были в памяти и H2 / MVStore (встроенное файловое хранилище). Хотя они отлично подходят для небольших проектов, их сложно найти для более крупных приложений производственного уровня. В частности, сложнее отлаживать содержимое такого хранилища (без инструментов), сложнее создавать резервные копии и реплицировать его, сложнее построить с ним многоузловую установку.

Итак, новое хранилище было необходимо, и неудивительно, что был выбран PostgreSQL: он предлагает несколько действительно хороших функций, которые я выделю в этой статье. Без них схема хранения, к которой я пришел, не была бы такой простой.

До сих пор все хранилища использовали формат сериализации двоичного макета, описанный в RFC 2 / BES, но вставлять его в PostgreSQL не особо интересно. Было бы легче разработать что-то подобное, и это сработало бы, но отладка все равно была бы сложной. Я знаю это точно. Более раннее воплощение Eventsourcing сделало это.

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

Таблица по типу сущности

Несмотря на то, что журнал представляет собой структуру, предназначенную только для добавления, он не обязательно должен содержать единый список журналируемых сущностей. Избавление от единой таблицы «сущностей» поможет получить ряд интересных преимуществ. Мы можем рассматривать журнал как набор из нескольких списков, которые мы добавляем. Фактически, мы дадим каждому типу сущности (каждой команде и каждому событию) отдельную таблицу.

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

Второе преимущество заключается в том, что он позволяет нам иметь фиксированный макет таблицы. Каждая раскладка сущности имеет фиксированное количество свойств. Итак, если мы сопоставим типы данных Eventsourcing с типами PostgreSQL, мы сможем сделать запрос сущностей тривиальной задачей.

Таблица для каждой версии типа объекта.

Eventsourcing использует строгую политику управления версиями для сущностей: все, что меняет основы макета сущности (имя, набор свойств, тип свойства, имя свойства), изменяет его версию (представленную хешем SHA1 или отпечатком пальца см. RFC 1 / ELF).

Это очень хорошо для выбранной нами карты. Поскольку свойства разных версий макета естественно различны, мы должны использовать разные структуры таблиц для представления каждой версии. И вот что делает eventsourcing-postgresql:

Эти имена таблиц используют хеш макета. Не особо удобное имя. Однако, если вы прокрутите эту Суть вправо, вы увидите, что мы наклеиваем удобочитаемый КОММЕНТАРИЙ на каждую таблицу, так что теперь их несложно сопоставить.

Использование составных типов

Сопоставление базовых типов с PostgreSQL довольно просто и не представляет большого интереса. Однако есть более интересный случай: что нам делать с сущностями, которые содержат свойства объекта? Даже одна из основных структур Eventsourcing, Timestamp, является объектом и присутствует в каждой отдельной сущности.

К счастью, PostgreSQL поддерживает то, что они называют составным типом - что-то похожее на запись или строку таблицы в значении.

Eventsourcing создает составной тип для каждого типа «объекта», используемого сущностью в системе, и прикрепляет к нему удобочитаемый КОММЕНТАРИЙ, и теперь мы можем легко запросить каждую запись в этой таблице:

Если бы вы проявили достаточно любопытства, чтобы заглянуть в правую часть этого Счета, вы могли бы заметить, что отметки времени выглядят как (0, -2657660719434565965) - это составной тип для отметки времени гибридных логических часов. Это не особенно читаемо (или даже не сравнимо), правда? Так что есть вспомогательная функция, которая также делает их удобочитаемыми:

Использование типов массивов

Еще одна интересная особенность PostgreSQL - это массив. Давайте проверим оператор CREATE TYPE за типом столбца test в приведенном выше примере:

Мы видим, что столбец list на самом деле представляет собой массив массивов TEXT, поэтому мы также можем легко использовать его в наших запросах:

Хотя нет ограничений на количество элементов в массиве PostgreSQL (помимо ограничения на размер поля), из-за ограничений JDBC и драйвера JDBC PostgreSQL мы ограничены в количестве элементов массива, которые мы можем вставить. Я до сих пор не понял, как создать массив непримитивного типа (подумайте о составных типах) в JDBC, поэтому вместо этого создаются соответствующие операторы INSERT для размещения списков, но он устанавливает верхний предел в 32 КБ элементов.

Не то чтобы это небольшое число - и большинству организаций, вероятно, следует избегать его достижения, но было бы интересно улучшить эту часть и снять это ограничение.

Представление

На момент написания этой статьи профилирование было минимальным, а производительность все же очень впечатляющая - до 1200–1500 операций в секунду на моем MacBook Pro Retina. Но для начала этого достаточно.

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

Будущее

Было бы действительно здорово получить механизм индексации PostgreSQL для Eventsourcing, разработанный в дополнение к имеющимся у нас хранилищам in-memory и MVStore / H2. Но даже без этого создание этой системы хранения определенно является отличной новостью для разработчиков приложений. Самоанализ того стоит.

В ожидании еще нескольких функций и некоторых исправлений (особенно некоторых в сторонних библиотеках) я выпущу 0.4. Но даже до этого не стесняйтесь взять последний снимок (они публикуются при каждой фиксации) и поиграть с ним. У нас также есть небольшая группа по IRC #eventsourcing (FreeNode) и Gitter - там можно ответить на многие ваши вопросы.