Skip to content

voluntas/duckdb-wasm-parquet

Repository files navigation

DuckDB-Wasm + Parquet + S3-compatible object storage + OPFS

Static Badge License

概要

DuckDB-Wasm をブラウザで使用して、 S3 互換オブジェクトストレージにある Parquet ファイルを読み込んで OPFS に保存し、 OPFS からファイルを読み込んで DuckdB-Wasm に登録し、DuckDB-Wasm で SQL でクエリを実行するお試し用のアプリです。

注意

  • TypeScript は適当ですので、参考にしないで下さい
  • SQL は適当ですので、参考にしないで下さい

とりあえず触ってみたい

GitHub Pages にアプリをデプロイしてあります。

DuckDB-Wasm + Parquet + S3-compatible object storage + OPFS

ローカルで動作させる方法

pnpm install
mv .env.template .env
# https://duckdb-wasm.shiguredo.jp/P78BHZM3MD3MV47JDZG47PB8PW.parquet を設定する
pnpm run dev
  • ブラウザで http://localhost:5173/ にアクセスして、Scan Parquet ボタンをクリックしてください
  • Samples (1%) ボタンをクリックして、サンプリングした 1% のデータを表示してみてください
  • Aggregation ボタンをクリックして、集計した結果を表示してみてください
  • Clear ボタンで OPFS のファイルを削除します

動作例

Image from Gyazo

インスタント検索

毎回 SQL を実行しています。

Image from Gyazo

CodeMirror を利用した SQL エディター

Vim キーバインディングを追加してます。

Image from Gyazo

Parquet ファイルについて

以下の URL でお試し用の Parquet ファイルを取得することができます。

https://duckdb-wasm.shiguredo.jp/P78BHZM3MD3MV47JDZG47PB8PW.parquet

この Parquet ファイルは時雨堂WebRTC SFU Sora が出力する クライアントから送られてくる WebRTC 統計情報Fluent Bit 経由で S3 互換オブジェクトストレージ へ保存し、 DuckDB で集約し Parquet ファイルとして出力したものです。 元々のログは JSONL 形式で gzip 圧縮です。

  • この Parquet ファイルは zstd で圧縮されています
  • この Parquet ファイルは Cloudflare R2 に保存されており、パブリックで誰でもアクセスすることができます
  • この Parquet ファイルの Access-Control-Allow-Originhttp://localhost:5173/ からのアクセスを許可しています
  • この Parquet ファイルのライセンスは CC BY-NC-ND 4.0 です

OPFS について

Origin private file system - Web APIs | MDN

安全にブラウザでファイルを取り扱う仕組みです。 Fetch APIStream API を利用して S3 互換のオブジェクトストレージから Parquet ファイルを取得し、ストリームで OPFS に保存しています。 保存したファイルを DuckDB へ登録し、アクセスできるようにします。

この仕組みを使う事で解析用のファイルを再度ダウンロードする必要がなくなります。

Safari

Safari の OPFS は FileSystemFileHandle: createWritable() method が実装されていないため、Safari では OPFS は利用せず毎回 fetch するようにしています。

231706 – Implement File System standard

DuckDB でアクセスしてみる

D SELECT * FROM parquet_scan('https://duckdb-wasm.shiguredo.jp/P78BHZM3MD3MV47JDZG47PB8PW.parquet');
┌──────────────────────┬──────────────────────┬─────────────────┬───┬───────────────────┬─────────────────────┐
│    connection_id     │          id          │      label      │ … │   rtc_timestamp   │      rtc_type       │
│       varcharvarcharvarchar     │   │      double       │       varchar       │
├──────────────────────┼──────────────────────┼─────────────────┼───┼───────────────────┼─────────────────────┤
│ F6WJ4SY2HD63Z25XD3…  │ DE5MC2JG3H2PK667ZG…  │ WebRTC SFU Sora │ … │ 1726394702721.481 │ media-playout       │
│ F6WJ4SY2HD63Z25XD3…  │ DE5MC2JG3H2PK667ZG…  │ WebRTC SFU Sora │ … │ 1726394702721.481 │ certificate         │
│ F6WJ4SY2HD63Z25XD3…  │ DE5MC2JG3H2PK667ZG…  │ WebRTC SFU Sora │ … │ 1726394702721.481 │ certificate         │
│ F6WJ4SY2HD63Z25XD3…  │ DE5MC2JG3H2PK667ZG…  │ WebRTC SFU Sora │ … │ 1726394702721.481 │ codec               │
│ F6WJ4SY2HD63Z25XD3…  │ DE5MC2JG3H2PK667ZG…  │ WebRTC SFU Sora │ … │ 1726394702721.481 │ codec               │
│ F6WJ4SY2HD63Z25XD3…  │ DE5MC2JG3H2PK667ZG…  │ WebRTC SFU Sora │ … │ 1726394702721.481 │ candidate-pair      │
│ F6WJ4SY2HD63Z25XD3…  │ DE5MC2JG3H2PK667ZG…  │ WebRTC SFU Sora │ … │ 1726394702721.481 │ candidate-pair      │
│ F6WJ4SY2HD63Z25XD3…  │ DE5MC2JG3H2PK667ZG…  │ WebRTC SFU Sora │ … │ 1726394702721.481 │ candidate-pair      │
│ F6WJ4SY2HD63Z25XD3…  │ DE5MC2JG3H2PK667ZG…  │ WebRTC SFU Sora │ … │ 1726394702721.481 │ candidate-pair      │
│ F6WJ4SY2HD63Z25XD3…  │ DE5MC2JG3H2PK667ZG…  │ WebRTC SFU Sora │ … │ 1726394702721.481 │ candidate-pair      │
│ F6WJ4SY2HD63Z25XD3…  │ DE5MC2JG3H2PK667ZG…  │ WebRTC SFU Sora │ … │ 1726394702721.481 │ candidate-pair      │
│ F6WJ4SY2HD63Z25XD3…  │ DE5MC2JG3H2PK667ZG…  │ WebRTC SFU Sora │ … │ 1726394702721.481 │ data-channel        │
│ F6WJ4SY2HD63Z25XD3…  │ DE5MC2JG3H2PK667ZG…  │ WebRTC SFU Sora │ … │ 1726394702721.481 │ data-channel        │
│ F6WJ4SY2HD63Z25XD3…  │ DE5MC2JG3H2PK667ZG…  │ WebRTC SFU Sora │ … │ 1726394702721.481 │ data-channel        │
│ F6WJ4SY2HD63Z25XD3…  │ DE5MC2JG3H2PK667ZG…  │ WebRTC SFU Sora │ … │ 1726394702721.481 │ data-channel        │
│ F6WJ4SY2HD63Z25XD3…  │ DE5MC2JG3H2PK667ZG…  │ WebRTC SFU Sora │ … │ 1726394702721.481 │ local-candidate     │
│ F6WJ4SY2HD63Z25XD3…  │ DE5MC2JG3H2PK667ZG…  │ WebRTC SFU Sora │ … │ 1726394702721.481 │ local-candidate     │
│ F6WJ4SY2HD63Z25XD3…  │ DE5MC2JG3H2PK667ZG…  │ WebRTC SFU Sora │ … │ 1726394702721.481 │ local-candidate     │
│ F6WJ4SY2HD63Z25XD3…  │ DE5MC2JG3H2PK667ZG…  │ WebRTC SFU Sora │ … │ 1726394702721.481 │ local-candidate     │
│ F6WJ4SY2HD63Z25XD3…  │ DE5MC2JG3H2PK667ZG…  │ WebRTC SFU Sora │ … │ 1726394702721.481 │ local-candidate     │
│          ·           │          ·           │        ·        │ · │         ·         │       ·             │
│          ·           │          ·           │        ·        │ · │         ·         │       ·             │
│          ·           │          ·           │        ·        │ · │         ·         │       ·             │
│ ZVWETDA641527D2A6T…  │ S31C88V2SX7M76DD6M…  │ WebRTC SFU Sora │ … │ 1726403015487.149 │ candidate-pair      │
│ ZVWETDA641527D2A6T…  │ S31C88V2SX7M76DD6M…  │ WebRTC SFU Sora │ … │ 1726403015487.149 │ data-channel        │
│ ZVWETDA641527D2A6T…  │ S31C88V2SX7M76DD6M…  │ WebRTC SFU Sora │ … │ 1726403015487.149 │ data-channel        │
│ ZVWETDA641527D2A6T…  │ S31C88V2SX7M76DD6M…  │ WebRTC SFU Sora │ … │ 1726403015487.149 │ data-channel        │
│ ZVWETDA641527D2A6T…  │ S31C88V2SX7M76DD6M…  │ WebRTC SFU Sora │ … │ 1726403015487.149 │ data-channel        │
│ ZVWETDA641527D2A6T…  │ S31C88V2SX7M76DD6M…  │ WebRTC SFU Sora │ … │ 1726403015487.149 │ inbound-rtp         │
│ ZVWETDA641527D2A6T…  │ S31C88V2SX7M76DD6M…  │ WebRTC SFU Sora │ … │ 1726403015487.149 │ inbound-rtp         │
│ ZVWETDA641527D2A6T…  │ S31C88V2SX7M76DD6M…  │ WebRTC SFU Sora │ … │ 1726403015487.149 │ inbound-rtp         │
│ ZVWETDA641527D2A6T…  │ S31C88V2SX7M76DD6M…  │ WebRTC SFU Sora │ … │ 1726403015487.149 │ inbound-rtp         │
│ ZVWETDA641527D2A6T…  │ S31C88V2SX7M76DD6M…  │ WebRTC SFU Sora │ … │ 1726403015487.149 │ outbound-rtp        │
│ ZVWETDA641527D2A6T…  │ S31C88V2SX7M76DD6M…  │ WebRTC SFU Sora │ … │ 1726403015487.149 │ outbound-rtp        │
│ ZVWETDA641527D2A6T…  │ S31C88V2SX7M76DD6M…  │ WebRTC SFU Sora │ … │   1726403011612.0 │ remote-inbound-rtp  │
│ ZVWETDA641527D2A6T…  │ S31C88V2SX7M76DD6M…  │ WebRTC SFU Sora │ … │   1726403014647.0 │ remote-inbound-rtp  │
│ ZVWETDA641527D2A6T…  │ S31C88V2SX7M76DD6M…  │ WebRTC SFU Sora │ … │   1726403013580.0 │ remote-outbound-rtp │
│ ZVWETDA641527D2A6T…  │ S31C88V2SX7M76DD6M…  │ WebRTC SFU Sora │ … │   1726403011612.0 │ remote-outbound-rtp │
│ ZVWETDA641527D2A6T…  │ S31C88V2SX7M76DD6M…  │ WebRTC SFU Sora │ … │   1726403015068.0 │ remote-outbound-rtp │
│ ZVWETDA641527D2A6T…  │ S31C88V2SX7M76DD6M…  │ WebRTC SFU Sora │ … │   1726403014750.0 │ remote-outbound-rtp │
│ ZVWETDA641527D2A6T…  │ S31C88V2SX7M76DD6M…  │ WebRTC SFU Sora │ … │ 1726403015487.149 │ media-source        │
│ ZVWETDA641527D2A6T…  │ S31C88V2SX7M76DD6M…  │ WebRTC SFU Sora │ … │ 1726403015487.149 │ media-source        │
│ ZVWETDA641527D2A6T…  │ S31C88V2SX7M76DD6M…  │ WebRTC SFU Sora │ … │ 1726403015487.149 │ transport           │
├──────────────────────┴──────────────────────┴─────────────────┴───┴───────────────────┴─────────────────────┤
│ 83911 rows (40 shown)                                                                  20 columns (5 shown) │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

記事

DuckDB/DuckDB-Wasm を利用した低コストでの可視化

ライセンス

Copyright 2024-2024, @voluntas

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.