Сегодня мы с нашими друзьями из Snaplet открываем исходники postgres-wasm
— запускаемый в браузере сервер PostgreSQL с полным набором функционала, включая сохранение состояния в браузере, восстановление из pg_dump
и логическую репликацию из удалённой базы данных.
Впервые Postgres в браузере запустили в Crunchy Data, их потрясающая версия выложена на HN месяц назад. Вместе со Snaplet мы решили сделать версию с открытым кодом. Посмотрим, как она разрабатывается и какой функционал мы добавили. Подробности — к старту нашего флагманского курса по Data Science.
Что такое Snaplet?
Snaplet — это данные с продакшена и предварительные базы данных, с которыми разработчики могут писать код, сосредоточившись на переносимости. Посмотрите, как с помощью Snaplet клонировать среды Supabase.
Демо
Прежде чем перейти к техническим деталям, попробуйте сами (внимание: загрузится ~30 Мб).
Postgres внутри браузера
Запускаем локально:
# From Snaplet Repo
git clone git@github.com:snaplet/postgres-wasm.git
cd postgres-browser/packages/pg-browser
npx serve
# From Supabase Fork
git clone git@github.com:supabase-community/postgres-wasm.git
cd postgres-wasm
git checkout web
cd packages/supabrowser
npx serve
И вводим в браузере localhost:3000.
Функционал
В нашей демоверсии — приятные плюшки!
- Postgres 14.5,
psql
,pg_dump
и т. д. - Сохранение/восстановление состояния в файл и из файла.
- Сохранение/восстановление состояния Postgres в хранилище и из IndexedDB.
- Быстрый запуск из файла состояния или полная перезагрузка эмулятора.
- Параметры конфигурирования памяти от 128 до 1024 Мб.
- Изменение размера шрифта для терминала.
- Загрузка файлов в эмулятор, включая дампы БД и CSV-файлы.
- Выгрузка файлов из эмулятора.
- Сетевое подключение от эмулятора к интернету.
- Входящий сетевой туннель в порт 5432 Postgres внутри эмулятора.
Для чего нужен postgres-wasm
?
Хороший вопрос. postgres-wasm
пока весит около 30 Мб, поэтому для общих вариантов применения запуск Postgres в браузере сейчас не так хорош. Но потенциал у него большой. Вот идеи, над которыми мы будем работать в ближайшие месяцы:
- Документация: для учебных руководств и демоверсий.
- Автономные данные: их запуск в браузере для автономного кеша аналогично sql.js или absurd-sql.
- Автономный анализ данных: его применение в дашборде для автономного анализа данных и диаграмм.
- Тестирование: функций PostgresSQL, триггеров, моделирования данных, логической репликации и т. д.
- Среды разработки: их применение (извлечение данных из продакшена или добавление на продакшен новых данных, функций, триггеров, представлений).
- Снимки: создание тестовой версии БД с образцом данных, а затем отправка её моментального снимка другим разработчикам.
- Поддержка: отправка снимков БД с проблемой в службу поддержки.
Обзор репозитория
Мы разделили репозиторий на три папки: виртуальная машина, веб-приложение и сетевой прокси.
Виртуальная машина
Создаём встраиваемую ВМ при помощи Buildroot. Наша ВМ — это урезанная сборка Linux с установленным Postgres. Смотрите исходный код.
Веб-приложение
Дальше запускаем её в браузере с помощью WASM и v86. Демоверсия приложения очень проста — это обычный HTML с простыми стилями. Смотрите исходный код.
Прокси
Запускать Postgres в браузере — это хорошо, а подключаться к Postgres с помощью pgAdmin — ещё лучше. К сожалению, в браузерах блокируется сетевой TCP-доступ к ВМ, поэтому проксируем трафик через веб-сокеты. Запускаем форк Websockproxy для взаимодействия эмулятора с интернетом, то есть преобразования данных, отправляемых через порт веб-сокета, в TCP-пакеты. С этим форком можно пробросить туннель на сервер Postgres. Смотрите исходный код.
Технические подробности
В ходе разработки postgres-wasm
обнаружилось много проблем.
WASM
Первая проблема: наша реализация — не чистый WASM. Мы пытались скомпилировать Postgres для WASM прямо из исходного кода, но это оказалось сложнее, чем мы ожидали.
В статье Crunchy на HN намекали на применяемый в работе подход — виртуализацию машины в браузере. Мы следовали этой стратегии с v86: с его помощью в браузере эмулируются аппаратное обеспечение и x86-совместимый ЦП.
Ошибки сегментации памяти (segfault) в PostgreSQL 14
PostgreSQL 13.3 с устаревшей версией Buildroot у нас запускалась быстро. А уже с версии PG14 не запускалась, и при инициализации выдавалась segfault.
Мы пробовали:
- ручное копирование файлов сборки для PG14 в старые версии Buildroot;
- сборку со многими более новыми копиями Buildroot;
- настройку параметров ядра и среды, например выделенный эмулятору объём памяти и т. д.
В итоге Фабиан, создатель v86, предложил отключить JIT-компиляцию для v86. Так решилась эта проблема. Он свёл её к багу в v86 и добавил устраняющее этот баг обновление. Проблема решилась и для текущего выпуска V86 — переключением управления памятью Postgres с posix на sysv.
Оптимизация времени запуска и размера образа
Запустив PG14 в эмуляторе, мы переключились на производительность. Размер образа эмулятора оказался слишком велик для браузерного инструмента. Как мы ни старались сжать снимок, загружать более 30 Мб, прежде чем увидеть какую-то интерактивность, — многовато.
Мы решили эту проблему: запустили минимальный образ Linux, а после инициализации загрузили оставшубся часть виртуальной машины динамически по HTTPS.
Делается это монтированием в ВМ сжатой файловой системы 9P. В 9P есть Python-скрипт. Скрипт берёт папку файловой системы, переименовывает каждый файл 8-символьным именем, и выдаёт файл filesystem.json
— структуру с файлами, исходными именами файлов, размерами и т. д. Затем этот сжатый вывод копируется в виртуальную машину. Чтобы загружать прямо из файловой системы 9P, мы модифицировали командную строку ядра и параметры загрузки v86. И даже поместили в 9P файл ядра. Все необязательные файлы при необходимости также загружаются в браузере асинхронно по HTTPS.
Исходное состояние уменьшилось, но его размер оставался в пределах 15–20 Мб. Мы обсудили это с Фабианом, и он указал на параметр ядра page_poison=on
, с помощью которого перед созданием снимков кеши очищаются, а в освобождённую память Linux вместо случайных байтов записываются произвольные. Поэтому неиспользуемая память сжимается намного эффективнее.
Каков итог всех этих изменений? Размер сжатого файла исходного состояния составляет около 12 Мб, в том числе состояние с активной сетью и Postgres 14.4 с загруженным psql
.
Без запуска Postgres мы получили исходное состояние ещё меньше. Но инициализация Postgres обычно дольше загрузки дополнительных мегабайтов. Мы предпочли меньшее время запуска.
Кроме того, без состояния выполнения каждое обновление страницы чревато снижением производительности, ведь Postgres нужно каждый раз инициализироваться. В нашей текущей версии, после того как исходное состояние загружено и кешировано браузером, ВМ обновляется почти мгновенно.
Сеть
Особенно трудно решалась проблема сети. По соображениям безопасности в браузерах поддерживается доступ к ограниченному набору портов. В нашем случае у браузеров порты 80/443, а у Postgres — 5432, а значит, возможности взаимодействовать с внешним миром не было.
WASM здесь мало чем полезен. Запущенным в браузере модулем WebAssembly, как правило, никаких действий вне его не производится, кроме вызова предоставляемых из JavaScript функций.
Однако в браузерах есть другой вариант подключения — веб-сокеты. Их преимущество заключается в персистентности, поэтому нужно было только понять, как проксировать трафик ВМ через подключение к веб-сокету.
В ВМ есть эмулированная сетевая карта ne2k-pci
и параметр запуска proxy_url
, который указывает на внешний сервер, где проксирование выполняется через веб-сокет. При этом подключения устанавливаются через порт веб-сокета, а затем принимаются необработанные Ethernet-пакеты, которые преобразуются в TCP/IP-пакеты и маршрутизируются между интернетом и ВМ.
Postgres WASM, прокси
После создания туннеля можно отправлять сетевой трафик во вне. Попробуем «запустить» сеть в ВМ, «выйти» из psql (cmd
+D
) и запустим ping 1.1.1.1
.
Postgres WASM в интернете
В большинстве ВМ v86 используется открытый прокси-сервер wss://relay.widgetry.org/
на основе websockproxy. Благодаря ему возможны взаимодействие ВМ с внешним миром, отработка всех базовых сценариев, например загрузка данных из других БД PostgreSQL с помощью pg_dump
и psql
или работа в качестве реплики «только для чтения» другой БД.
Но с ним невозможна маршрутизация входящего трафика в ВМ, запускаемую локально на ноутбуке. Например, нельзя подключить локальный PgAdmin4
к экземпляру PostgreSQL в браузере. Поэтому мы дублировали прокси-сервер и в качестве обратного прокси-сервера добавили nginx.
Postgres WASM с psql
Обратным прокси-сервером прослушивается ряд портов, каждый из которых сопоставляется с ВМ в определённом, нужном нам приватном адресе.
Простое решение — взять последние два сегмента приватного IP-адреса и сопоставить с номером порта. Так, если приватный IP-адрес 10.5.6.123, подключаемся к прокси-серверу в порте 6123 и оттуда направляемся в PostgreSQL, запускаемую на порте 5432 с приватным IP 10.5.6.123. Сокращённые адреса дополняются, поэтому 10.5.6.2 сопоставляется с портом 6002, а 10.5.6.44 — с 6044.
Postgres WASM с удалённым входом в систему
Отправка команд из пользовательского интерфейса браузера в эмулятор
После запуска эмулятор, по словам Фабиана, «выполняется как процесс типа «чёрный ящик», а единственный способ взаимодействия пользовательского интерфейса и запущенной в эмуляторе операционной системы — это serial0_send
для отправки нажатий клавиш через эмулируемый последовательный порт. Либо функция create_file
, которая загружает файл в запущенный эмулятор.
Чтобы после восстановления файла состояния перезапустить сеть, нужно выполнить короткий скрипт для отмены привязки и повторной привязки эмулированной сетевой карты. Так она получает новый mac-адрес и, следовательно, уникальный приватный IP-адрес на прокси-сервере. И затем запустить стандартную команду перезапуска сети. Можно было бы запустить этот скрипт, отправив в эмулятор нажатия клавиш, но это громоздко. Если пользователь в командной строке psql
, отправляем \! script_command
. А если в командной строке ОС?
Мы решили проблему созданием папки \inbox
и добавлением в неё слушателя. При поступлении туда файла, который заканчивается на .sh
(скрипт оболочки), применяем к файлу chmod +x
, выполняем его, а затем удаляем. Так команды запускаются «в фоновом режиме», не затрагивая пользовательский интерфейс.
Попробуйте сами!
Подключитесь к экземпляру Postgres, запускаемому в браузере другого пользователя:
- Найдите кого-нибудь.
- Пусть он перейдёт по wasm.supabase.com.
- Запустит сеть и получит её адрес.
- Задаст пароль для своей БД
ALTER ROLE postgres WITH PASSWORD 'my_password';
. - Откройте терминал на своём компьютере и запустите
psql postgres://postgres:my_password@proxy.wasm.supabase.com:<PORT>
. - Вот и всё! Теперь вы подключаетесь к удалённому экземпляру Postgres внутри ВМ, в браузере, с терминала своего компьютера.
Репликация БД
Ещё круче — реплицировать данные из онлайн — БД PostgreSQL (например, проекта Supabase) в PostgreSQL, запускаемую в браузере. И, наоборот, используя логическую репликацию Postgres. То же относится к любой другой БД Postgres — просто используйте в качестве цели URL-адрес прокси-сервера.
Что дальше?
Пока всё это очень экспериментально, но с большим потенциалом. Чтобы поучаствовать, свяжитесь с нами или командой Snaplet. Они проделывают невероятную работу, и мы получили огромное удовольствие от сотрудничества с ними.
А мы научим работать с данными, чтобы вы прокачали карьеру или стали востребованным IT-специалистом:
Чтобы посмотреть все курсы, кликните по баннеру:
- Профессия Data Scientist
- Профессия Data Analyst
- Курс «Математика для Data Science»
- Курс «Математика и Machine Learning для Data Science»
- Курс по Data Engineering
- Курс «Machine Learning и Deep Learning»
- Курс по Machine Learning
Python,
- Профессия
Fullstack-разработчик на Python - Курс «Python для
веб-разработки » - Профессия
Frontend-разработчик - Профессия
Веб-разработчик
Мобильная разработка
Java и C#
- Профессия
Java-разработчик - Профессия
QA-инженер на JAVA - Профессия C#-разработчик
- Профессия Разработчик игр на Unity
От основ — в глубину
А также
Комментарии (7)
entze
26.10.2022 10:39А как у WASM с кешрованием на локальном устройстве? Если я на мобильном (WASM поддерживается мобильными браузерами) скачал эти 30Мб, я в следующий раз снова буду качать?
Подключитесь к экземпляру Postgres, запускаемому в браузере другого пользователя
Подождите ка... распределенные сервисы не требующие установки приложений, работающие в браузере?
RPG18
26.10.2022 12:10я в следующий раз снова буду качать?
Это обычная статика. Как настроите так и будет. Проблема в том, что эти 30Mb нужно скомпилировать. Кеширование скомпилированного кода это другой вопрос https://developer.mozilla.org/en-US/docs/WebAssembly/Caching_modules
Через прокси можно и вбраузере. Но может быть придут к Docker+Wasm (Beta)
emaxx
Эмулятор на эмуляторе? Вот это химера.