Пет-проект: как запустить JavaScript-код внутри базы данных

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

Sibedge поощряет разработку пет-проектов по нескольким причинам:
  • Они приносят разработчикам удовольствие и позволяют им развиваться.
  • Мотивируют инженеров на собственные начинания.
  • Такая политика привлекает в компанию молодых талантливых специалистов, которые хотят реализоваться через собственные продукты.

Недавно наш .NET-инженер Алексей представил свою авторскую разработку — PLV8 Framework. Он позволяет упростить работу с гибкой и иерархической структурой данных, особенно в языках со статической типизацией. С его помощью можно вести полноценную разработку на стороне баз данных, в том числе — командную. Также фреймворк помогает снизить трудозатраты при решении типовых задач CRUD (англ. Create, Read, Update, Delete). Сначала над проектом работал только Алексей, но позже к разработке подключились ещё три инженера Sibedge.

Идея пет-проекта

Что такое PLV8? Это расширение для СУБД PostgreSQL, позволяющее выполнять функции на языке JavaScript. В названии PLV8 две составляющих: PL (Procedure Language — процедурный язык) и V8 (движок JavaScript с открытым исходным кодом).

Готовых пакетов для установки PLV8 в PostgreSQL актуальной версии не было, поэтому его собирали вручную из исходников с GitHub. Это занимало массу времени, расходовало трафик и требовало много места на диске для хранения временных файлов. Для простой и быстрой установки расширения PLV8 инженеры Sibedge создали инструмент для Linux (https://github.com/sibedge-llc/plv8-build/).

Проблемы разработки на стороне баз данных и способы решения

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

PLV8 Framework претендует на то, чтобы решить все эти проблемы. Идея такая: запускать JavaScript-код локально с помощью Node.js для разработки, отладки и тестирования. Развёртывание выполнять средствами Node.js — скрипт отправляет исходный код в хранимые процедуры базы данных. Node.js использует тот же движок V8, что и расширение PLV8. Это гарантирует, что результаты выполнения не будут отличаться.

PLV8 позволяет обрабатывать SQL-запросы при помощи команды plv8.execute. При выполнении кода локально, PLV8 Framework подменяет команду на обращение ко внешней СУБД при помощи npm-библиотеки pg-native. Главное требование: библиотека должна позволять работать с PostgreSQL синхронно, поскольку весь код PLV8 — синхронный.

Для unit-тестирования PLV8 Framework позволяет выполнять plv8.execute через SQLite (npm-пакет sqlite-sync), а также использовать mock.

Применение для CRUD-операций

PLV8 Framework предоставляет не только средства разработки новых функций на JavaScript. Для выполнения операций CRUD фреймворк включает в себя универсальные функции, которые будут работать в любой СУБД на любом проекте. Данные для изменения принимаются в формате JSON, где ключи соответствуют колонкам таблицы БД.

Для чтения данных Алексей разработал собственный универсальный GraphQL-сервер. Основная часть логики написана на PLV8 Framework, часть — на .NET. Поля запроса соответствуют таблицам БД, вложенные поля — колонкам таблицы, а также связанным таблицам по внешнему ключу, причём в обе стороны. Сервер возвращает описание схемы данных для GraphQL-клиентов, а также поддерживает:
  • фильтрацию (в том числе, по вложенным полям);
  • сортировку;
  • постраничный вывод;
  • группировку;
  • агрегатные функции;
  • фильтрацию по сгруппированным строкам (HAVING).

Пример запроса:
Screenshot 2021-08-18 at 22.54.42.png
Функции для CRUD-операций поддерживают авторизацию. Фреймворк позволяет разграничить доступ к таблицам БД в зависимости от роли пользователя, как на запись, так и на чтение. Для таблиц с полем ID пользователя и связанных таблиц есть возможность открыть доступ пользователю только к «своим» записям.

Производительность

Команда провела серию тестов получения данных с несколькими уровнями вложённости с помощью PLV8 и SQL-запроса с агрегатными функциями СУБД. Количество возвращаемых объектов верхнего уровня менялось от 1 до 20 000. Тесты показали, что на больших объёмах данных (18–20 тысяч элементов) PLV8 проигрывает SQL-запросам в скорости выполнения примерно на 30%. При количестве в диапазоне от 8 до 11 тысяч элементов PLV8 не уступает в производительности, а при меньших объёмах данных — даже работает быстрее.

Screenshot 2021-08-18 at 22.57.06.png

Практическое применение

При использовании PLV8 Framework нет необходимости разрабатывать весь бэкенд на этой технологии. Можно использовать классическую архитектуру приложения (например, Asp.NET Core), а функции на PLV8 использовать только там, где это действительно нужно. Например, для CRUD-операций. В этом случае затраты на разработку бэкенда будут сведены к минимуму, так как универсальные функции, включенные во фреймворк, позволяют реализовать 90–95% функциональности.

Также собственные функции на PLV8 целесообразно писать там, где требуется гибкая многоуровневая структура данных (например, для построения отчётов). Это делает разработку проще и быстрее, особенно если в бэкенде используются языки со статической типизацией: для таких задач больше не нужно писать классы под каждую структуру данных. СУБД возвращает результат функции на PLV8 в формате JSON, и бэкенд отправляет его как есть. 

Для многих других задач (алгоритмические задачи, взаимодействие с другими сервисами) реализация на бэкенде, например на .NET, предпочтительнее. Тем не менее, PLV8 Framework позволяет решить часть задач разработки бэкенда за меньшее время и с меньшими усилиями. И в некоторых случаях разница существенная.

Планы на будущее 

Алексей хочет и дальше развивать фреймворк и расширять его функциональность. В планах реализовать генерацию TypeScript API из Swagger, расширить возможности GraphQL-сервера. Сегодня PLV8 Framework уже используется как во внутренних проектах Sibedge, так и в проектах клиентов компании.