PostgreSQLをブラウザ上で実行するOSS「Postgres-WASM」を触ってみた
PostgreSQLをブラウザ上で実行する「Postgres-WASM」がオープンソースとして公開されました。
We've open sourced Postgres WASM
Hat tip to all the amazing engineers that made it possible for us to glue this together, including those at @supabase and @_snaplet
Join the discussion on HN! pic.twitter.com/u4tIceRF66
— Peter Cilliers-Pistorius ?? (@appfactory) October 3, 2022
2022年8月にCrunchy DataがPOSTGRES PLAYGROUND としてブラウザ版PostgreSQLをサービス提供したことを受け、Supabase社とSnaplet社がオープンソース版としてリリースしたのが「Postgres-WASM」です。
クローズド・オープンソースという違いはあるものの、Crunchy DataとSupabase/Snapletがとったアプローチは同じです。
Crunchy's HN post provided some hints about the approach they took, which was to virtualize a machine in the browser. We pursued this strategy too, settling on v86 which emulates an x86-compatible CPU and hardware in the browser.
Postgres-WASM を少し触ってみたので、紹介します。
Postgres-WASMができること
Postgres-WASM では PostgreSQL 14が動作しています。もろもろのSQLを実行できるのはもちろんのこと、状態を保存・リストアできたり、ブラウザ外からの通信、例えば、psqlで接続したり、外部データベースとレプリケーションを組むこともできます。
触ってみる
デモページ
Supabase社がデモページにアクセスすると、すぐに体験できます。
数十MBのダウンロードを伴うので注意しましょう。
ローカルに構築
成果物はGitHubでOSSとして公開されているので、シュッと試せます。
$ git clone https://github.com/snaplet/postgres-wasm.git $ cd postgres-wasm/packages/runtime && npx serve ┌─────────────────────────────────────────────────────┐ │ │ │ Serving! │ │ │ │ - Local: http://localhost:3000 │ │ - On Your Network: http://192.168.101.123:3000 │ │ │ │ Copied local address to clipboard! │ │ │ └─────────────────────────────────────────────────────┘
※素のOSS版
以降では、Postgres-WASM の 開発に携わった Supabase 社の Mark Burggraf 氏の詳細な解説記事をベースに技術面を確認し、機能・UIがリッチな同社の提供する次のデモサイトを前提に話を進めます。
WASM 向けにコンパイルされているのは何?
Postgres-WASM は名前からWASMを使っていることが伺えますが、PostgreSQL が WASM 向けにコンパイルされているわけではありません。 x86互換のCPU・ハードウェアをエミュレートする v86 が WASM ランタイム上で実行されています。
このv86 上で PostgreSQL がインストールされた x86 Linux の仮想マシンが動いているので、ブラウザ上でPostgreSQLが動いているように見えます。
デモページにアクセスすると、ブラウザの開発ツールから、370kBの v86.wasm をダウンロードしているのを確認できます。
CTRL-D で PostgreSQL のシェルインターフェースを抜けると、PostgreSQL をホストする Linux にアクセスできます。
postgres=# Ctrl-D \q # uname -a Linux buildroot 5.17.15 #1 SMP PREEMPT Tue Sep 27 03:56:40 UTC 2022 i686 GNU/Linux # cat /etc/os-release NAME=Buildroot VERSION=2022.08 ID=buildroot VERSION_ID=2022.08 PRETTY_NAME="Buildroot 2022.08" # ls -lh /usr/bin/postgres -rwxr-xr-x 1 1000 1000 6.8M Sep 27 04:10 /usr/bin/postgres # ps PID USER COMMAND 1 root init 2 root [kthreadd] 3 root [rcu_gp] 4 root [rcu_par_gp] 5 root [netns] 6 root [kworker/0:0-eve] 7 root [kworker/0:0H-ev] 8 root [kworker/u2:0-ev] 9 root [kworker/0:1H-ev] 10 root [mm_percpu_wq] 11 root [rcu_tasks_kthre] 12 root [ksoftirqd/0] 13 root [rcu_preempt] 14 root [migration/0] 15 root [cpuhp/0] 16 root [kdevtmpfs] 17 root [inet_frag_wq] 18 root [kauditd] 19 root [kworker/0:1-eve] 20 root [oom_reaper] 21 root [writeback] 22 root [kcompactd0] 23 root [kblockd] 24 root [ata_sff] 25 root [md] 26 root [kworker/u2:1-ev] 27 root [rpciod] 28 root [xprtiod] 29 root [cfg80211] 30 root [kworker/u2:2] 31 root [kswapd0] 32 root [nfsiod] 34 root [kworker/0:2-eve] 35 root [mld] 36 root [ipv6_addrconf] 77 root /sbin/syslogd -n 81 root /sbin/klogd -n 128 root udhcpc -t1 -A3 -b -R -O search -p /var/run/udhcpc.eth0.pid -i eth0 -x hostname:buildroot 138 postgres /usr/bin/postgres -D /var/lib/pgsql 140 postgres postgres: checkpointer 141 postgres postgres: background writer 142 postgres postgres: walwriter 143 postgres postgres: autovacuum launcher 144 postgres postgres: stats collector 145 postgres postgres: logical replication launcher 147 root {exe} ash /usr/bin/watcher.sh 150 root -sh 192 root sleep 2 193 root ps # netstat -l Active Internet connections (only servers) Proto Recv-Q Send-Q Local Address Foreign Address State tcp 0 0 0.0.0.0:postgres 0.0.0.0:* LISTEN tcp 0 0 :::postgres :::* LISTEN Active UNIX domain sockets (only servers) Proto RefCnt Flags Type State I-Node Path unix 2 [ ACC ] STREAM LISTENING 9097 /tmp/.s.PGSQL.5432 # dmesg [ 15.592300] echo (148): drop_caches: 1 [ 21.444400] sh (173): drop_caches: 3 #
PostgreSQL を v86 に 移植するにあたり、v86 の JIT のバグで SEGV が起きたり、IPC のメモリ共有を Posix から System V に変えるなど、 v86 固有の問題を回避したそうです。
VM イメージサイズの削減
v86は Plan 9 で開発された 9p ファイルシステムをサポートしています。
A 9p filesystem is supported by the emulator, using a virtio transport. Using it, files can be exchanged with the guest OS
# mount host9p on / type 9p (rw,dirsync,relatime,loose,access=client,trans=virtio) devtmpfs on /dev type devtmpfs (rw,relatime,size=54360k,nr_inodes=13590,mode=755) proc on /proc type proc (rw,relatime) devpts on /dev/pts type devpts (rw,relatime,gid=5,mode=620,ptmxmode=666) tmpfs on /dev/shm type tmpfs (rw,relatime,mode=777) tmpfs on /tmp type tmpfs (rw,relatime) tmpfs on /run type tmpfs (rw,nosuid,nodev,relatime,mode=755) sysfs on /sys type sysfs (rw,relatime) # df -h Filesystem Size Used Available Use% Mounted on host9p 256.0G 73.1M 255.9G 0% / devtmpfs 53.1M 0 53.1M 0% /dev tmpfs 53.4M 0 53.4M 0% /dev/shm tmpfs 53.4M 40.0K 53.4M 0% /tmp tmpfs 53.4M 16.0K 53.4M 0% /run
VMイメージサイズが大きいと、初期化に時間がかかるため、ブートイメージを切り詰め、初期化に必須でないファイル群は、初期化後にオンデマンドで遅延ロードします。
これらの努力により、PostgreSQL のプログラム群を含んだ初期状態ファイルは30MBから12MBに圧縮されました。
デモ画面のトラフィックを確認すると、 https://wasm.supabase.com/state/state-128.bin.zst という 12.4MB の Zstandard (Zstd) 圧縮されたファイルをダウンロードしています。 このファイルが、初期状態ファイルと思われます。
ネットワーク
v86 はデフォルトではトラフィックが仮想イメージ内に閉じているため、VM外との通信には工夫が必要です。
# route Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface default 10.5.0.1 0.0.0.0 UG 0 0 0 eth0 10.5.0.0 * 255.255.0.0 U 0 0 0 eth0 # ip addr show eth0 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN qlen 1000 link/ether 00:22:15:80:1f:7f brd ff:ff:ff:ff:ff:ff inet 10.5.6.146/16 brd 10.5.255.255 scope global eth0 valid_lft forever preferred_lft forever inet6 fe80::222:15ff:fe80:1f7f/64 scope link valid_lft forever preferred_lft forever # ping 8.8.8.8 PING 8.8.8.8 (8.8.8.8): 56 data bytes ping: sendto: Network unreachable
v86はネットワークカード(ne2k-pci
)がエミュレートされており、WebSocketのプロキシーサーバー経由でVM外と通信できます。
# lspci -k 00:00.0 Class 0600: 8086:1237 00:12.0 Class 0300: 1234:1111 00:06.0 Class 0002: 1af4:1049 virtio-pci 00:05.0 Class 0200: 10ec:8029 ne2k-pci 00:01.0 Class 0601: 8086:7000 00:07.0 Class 0680: 8086:7113
v86/networking.md at master · copy/v86 · GitHub
ブラウザからVirtual Machineのネットワークを起動します。
仮想シェル下部にプロキシーサーバーのホスト・ポート情報が表示されます。
host:proxy.wasm.supabase.com port:6159 psql postgres://postgres@proxy.wasm.supabase.com:6159
0.0.0.0/0 へのルーティングを確認すると、ゲートウェイが wasm-proxy に変わりました。
# route Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface default wasm-proxy.loca 0.0.0.0 UG 0 0 0 eth0 10.5.0.0 * 255.255.0.0 U 0 0 0 eth0
この状態で ping すると成功します。
# ping 8.8.8.8 PING 8.8.8.8 (8.8.8.8): 56 data bytes 64 bytes from 8.8.8.8: seq=0 ttl=59 time=169.100 ms 64 bytes from 8.8.8.8: seq=1 ttl=59 time=166.600 ms ^C --- 8.8.8.8 ping statistics --- 2 packets transmitted, 2 packets received, 0% packet loss round-trip min/avg/max = 166.600/167.850/169.100 ms #
ただし、リファレンス実装のプロキシサーバー(GitHub - benjamincburns/websockproxy)は、フォワード・プロキシにしか対応していません。
psql でVM内のPostgreSQLに接続するなど、外からVM内に通信できるように、postgres-wasm ではリバース・プロキシに対応したフォーク版が同梱されています。
※ 引用元
VM側で postgres
ユーザーのパスワードを設定し(ALTER ROLE postgres WITH PASSWORD 'my_password';
)、VM外から psql で接続してみましょう。
$ psql postgres://postgres@proxy.wasm.supabase.com:6159 Password for user postgres: psql (14.5) Type "help" for help. postgres=# select version(); version ------------------------------------------------------------------------------------------------------------------------------------ PostgreSQL 14.5 on i686-buildroot-linux-musl, compiled by i686-buildroot-linux-musl-gcc.br_real (Buildroot 2022.08) 12.1.0, 32-bit (1 row) postgres=# \x Expanded display is on. postgres=# select * from pg_stat_activity where usename = 'postgres'; -[ RECORD 1 ]----+----------------------------------------------------------- datid | datname | pid | 145 leader_pid | usesysid | 10 usename | postgres application_name | client_addr | client_hostname | client_port | backend_start | 2022-09-27 12:18:18.7967+00 xact_start | query_start | state_change | wait_event_type | Activity wait_event | LogicalLauncherMain state | backend_xid | backend_xmin | query_id | query | backend_type | logical replication launcher -[ RECORD 2 ]----+----------------------------------------------------------- datid | 12971 datname | postgres pid | 167 leader_pid | usesysid | 10 usename | postgres application_name | psql client_addr | client_hostname | client_port | -1 backend_start | 2022-09-27 12:18:21.7741+00 xact_start | query_start | 2022-09-27 12:18:40.004+00 state_change | 2022-09-27 12:18:41.292599+00 wait_event_type | Client wait_event | ClientRead state | idle backend_xid | backend_xmin | query_id | query | ALTER ROLE postgres WITH PASSWORD 'my_password'; backend_type | client backend -[ RECORD 3 ]----+----------------------------------------------------------- datid | 12971 datname | postgres pid | 363 leader_pid | usesysid | 10 usename | postgres application_name | psql client_addr | 10.5.0.1 client_hostname | client_port | 49874 backend_start | 2022-09-27 12:19:57.039+00 xact_start | 2022-09-27 12:22:07.378699+00 query_start | 2022-09-27 12:22:07.378699+00 state_change | 2022-09-27 12:22:07.378699+00 wait_event_type | wait_event | state | active backend_xid | backend_xmin | 733 query_id | query | select * from pg_stat_activity where usename = 'postgres'; backend_type | client backend
このネットワーク機能を有効にすると、ブラウザ上のPostgreSQLをソース・ターゲットとしてレプリケーションを組むこともできます。
※ 引用元
最後に
Supabase社とSnaplet社がオープンソース化したブラウザ版PostgreSQL「Postgres-WASM」を紹介しました。
2022年8月にCrunchy Dataがブラウザ版PostgreSQLを公開したあと、9月にはChrome開発チームがブラウザ版SQLiteの開発計画を発表するなど、データベース関連のWASM化がにわかに盛り上がっています。
出自が組み込み向けのSQLiteに比べ、クライアント・サーバー型のPostgreSQLを直接WASM化するのは、少しハードルが高そうです。
現状、Postgres-WASMは現時点の技術的な制約のために、v86上で仮想マシンを起動して、富豪的に動かしています。
今後、WASMのできることがリッチになったり、新しい技術的アイデアが出てくると、PostgreSを直接WASM向けにコンパイルして、いい感じに動くようになるのかなぁという印象を持ちました。
それでは。
参考
- Postgres WASM | Hacker News
- Postgres WASM by Snaplet and Supabase
- PostgreSQLをWebブラウザ上で動かす「Postgres-WASM」、オープンソースで公開。Webブラウザ上のx86仮想マシンで実行、レプリケーションも可能 - Publickey
- WebAssembly化したPostgreSQLをWebブラウザ上で実際に動かして学ぶ「Postgres playground」をCrunchy Dataが公開 - Publickey
- Chrome開発チームがSQLiteチームとWebAssembly版SQLiteを開発中。Webブラウザ上からのファイル書き込みで永続化も可能。Web SQL APIの代替として - Publickey
- Postgres Playground で pgbench を走らせよう!(第35回PostgreSQLアンカンファレンス@オンライン 発表資料)