01
READY は状態の境界。
新しい READY は以前の処理が終わるまで待ってからサーバー状態を更新します。後続イベントはその後に開始され、古い処理による上書きを防ぎます。
Fjord の舞台裏
Rust 製の Bot、自社開発のグラフィックスエンジン。そして Fjord を支えるフレームワーク、ライブラリ、データベース。
私たちが独自に開発しているものと、Fjord を構成する技術をご紹介します。
Fjord
Fjord は Discord イベントを順序どおりに処理し、独立したタスクを並行実行します。Twilight はボットを Discord に接続し、サーバーデータをローカルで保持します。Dragonfly は処理待ちのイベントを保存し、ゲートウェイ接続の調整を支えます。
01
新しい READY は以前の処理が終わるまで待ってからサーバー状態を更新します。後続イベントはその後に開始され、古い処理による上書きを防ぎます。
02
同じサーバーのイベントは到着順に開始されます。前の処理が終わるまで次を待たせ、状態変更が追い越し合うのを防ぎます。
03
ディスパッチャーはキーで処理を区別します。異なるサーバーは同時に進み、同じキーの処理だけが順番を待ちます。
04
同じメッセージが AutoMod とアクティビティログの両方に届きます。それぞれが自分の役割として別々に処理します。
05
処理待ちの Discord イベントとそのデータは Dragonfly に保存されます。AutoMod とアクティビティログも、一時的な状態にこの共有ストアを使用します。タスクの実行は引き続きボットが制御します。
06
有効な AutoMod フィルターが一致すると、そのメッセージの検査を終了します。他のフィルターが同じイベントに二度目の措置を行うことはありません。
07
Twilight はサーバー、メンバー、ロール、チャンネル、絵文字、プレゼンス、ボイス状態をメモリ内で最新に保ちます。メッセージはこのキャッシュに含まれません。
08
インタラクションはサーバーイベントとは独立してスケジュールされます。READY は各ゲートウェイ接続内で状態の境界を定めます。
09
Dragonfly は各シャード、つまりゲートウェイ接続の担当を調整します。引き継ぎ後、以前の担当は以降のイベントを取得したり完了したりできません。
01 / 独自開発
Fjell は Rust で独自開発したグラフィックエンジンです。背景、アバター、テキストを組み合わせて、個別のウェルカムカードを生成します。
画像の生成方法を自分たちで制御したい。そのため、Fjord の用途に合わせた仕組みを開発し、自由な表現を可能にしました。
コスト効率も同じく重要です。Fjell は品質を損なわずに最大限の性能を発揮し、生成時の CPU とメモリ使用量を可能な限り効率化します。これにより、サーバーコストを大幅に削減できます。
独自の実装はピクセル処理にまで及びます。自作の SIMD ループが AVX2、FMA、BMI1/BMI2 を使用し、複数のピクセルを並列に計算します。
性能測定
4 段階の負荷、制御されたカードの変化、同一のテスト構成です。
| 並列数 | カード/秒 | カード/分 | レンダー平均 | レンダー P50 | レンダー P95 | レンダー P99 | 合計平均 | 合計 P50 | 合計 P95 | 合計 P99 | CPU 平均 | RAM 平均 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 797,48 | 47.849 | 1,062 ms | 1,061 ms | 1,226 ms | 1,242 ms | 1,253 ms | 1,251 ms | 1,428 ms | 1,473 ms | 88,3 % | 381,2 MiB |
| 2 | 1.577,95 | 94.677 | 1,038 ms | 1,004 ms | 1,225 ms | 1,421 ms | 1,266 ms | 1,236 ms | 1,470 ms | 1,715 ms | 173,5 % | 360,1 MiB |
| 4 | 3.173,00 | 190.380 | 1,023 ms | 1,011 ms | 1,188 ms | 1,230 ms | 1,259 ms | 1,243 ms | 1,448 ms | 1,557 ms | 344,9 % | 375,3 MiB |
| 8 | 5.850,25 | 351.015 | 1,050 ms | 1,026 ms | 1,194 ms | 1,290 ms | 1,366 ms | 1,349 ms | 1,578 ms | 1,803 ms | 647,0 % | 385,8 MiB |
実測スループットと理想的な線形スケーリングの比較。
| ワーカー | 実測 | 理想線形 | スケーリング | 効率 |
|---|---|---|---|---|
| 1 | 797,48 | 797,48 | 1,00× | 100,0 % |
| 2 | 1.577,95 | 1.594,96 | 1,98× | 98,9 % |
| 4 | 3.173,00 | 3.189,92 | 3,98× | 99,5 % |
| 8 | 5.850,25 | 6.379,84 | 7,34× | 91,7 % |
4 回のローカルテストはいずれも AMD Ryzen 9 9950X 上で 60 秒間実行し、通常の色またはイラストを使った 100 種類の軽量な背景を使用しました。ファイルサイズは約 100~300 KB、標準的には約 200 KB です。Liquid Layer は無効、フォントは固定とし、カードごとにテキストと円の色およびシェーダー位置だけを変化させました。
レンダリング時間には画像生成に加えて PNG のエンコードと圧縮が含まれ、完成した PNG が得られるまでを計測しています。ネットワーク転送とダウンロード時間は含みません。
負荷生成側はテストに応じて 1、2、4、8 件の並列要求を使用しました。新しい要求は前の PNG を完全に受信して検証した後に開始しました。表示した CPU 使用率はこの負荷条件で発生した値です。グラフィックエンジンは処理を複数コアへ分散できました。
* 記載条件での社内測定であり、独立した検証は行われていません。保証された特性、保証、または性能上の確約を構成するものではありません。実際の性能はシステム、負荷、環境によって異なります。
高負荷ランダムテスト
1,000 種類の背景、237 種類のフォント、リクエストごとのランダム効果を使う、より変化の大きいテスト構成です。
| 並列数 | カード/秒 | カード/分 | レンダー平均 | レンダー P50 | レンダー P95 | レンダー P99 | 合計平均 | 合計 P50 | 合計 P95 | 合計 P99 | CPU 平均 | RAM 平均 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 92,65 | 5.559 | 9,775 ms | 10,478 ms | 12,781 ms | 13,401 ms | 10,788 ms | 11,553 ms | 14,006 ms | 14,784 ms | 93,6 % | 1.205,6 MiB |
| 2 | 184,53 | 11.072 | 9,944 ms | 10,621 ms | 12,858 ms | 13,550 ms | 10,832 ms | 11,576 ms | 13,938 ms | 14,698 ms | 189,1 % | 1.278,8 MiB |
| 4 | 358,82 | 21.529 | 10,209 ms | 10,855 ms | 13,240 ms | 13,997 ms | 11,138 ms | 11,857 ms | 14,363 ms | 15,212 ms | 377,7 % | 1.300,9 MiB |
| 8 | 660,52 | 39.631 | 10,897 ms | 11,610 ms | 14,343 ms | 15,336 ms | 12,101 ms | 12,889 ms | 15,838 ms | 16,880 ms | 744,8 % | 1.308,6 MiB |
実測スループットと理想的な線形スケーリングの比較。
| ワーカー | 実測 | 理想線形 | スケーリング | 効率 |
|---|---|---|---|---|
| 1 | 92,65 | 92,65 | 1,00× | 100,0 % |
| 2 | 184,53 | 185,30 | 1,99× | 99,6 % |
| 4 | 358,82 | 370,60 | 3,87× | 96,8 % |
| 8 | 660,52 | 741,20 | 7,13× | 89,1 % |
4 回のローカルテストはいずれも同じ AMD Ryzen 9 9950X 上で 60 秒間実行しました。各リクエストは 800 KB~2.4 MB の高負荷な背景 1,000 種類と 237 種類のフォントから選択し、テキスト、透明度、フォントとアバターのサイズ、アウトライン、テキストと円の色、シェーダーの角度・強さ・色強度を変化させました。約半数のカードでは Liquid Layer も有効にし、色、不透明度、ぼかしをランダム化しました。
負荷生成側は閉ループで 1、2、4、8 個の並列ワーカーを使用しました。各ワーカーは前の PNG を完全に受信して検証した後に次のリクエストを開始しました。すべての 60 秒テストはエラー、タイムアウト、無効な PNG なしで完了しました。
レンダリング時間には画像生成に加えて PNG のエンコードと圧縮が含まれ、完成した PNG が得られるまでを計測しています。ネットワーク転送とダウンロード時間は含みません。
* 記載条件での社内測定であり、独立した検証は行われていません。保証された特性、保証、または性能上の確約を構成するものではありません。実際の性能はシステム、負荷、環境によって異なります。
02 / 技術の基盤
独自エンジンに加えて、Rust、Leptos、TypeScript、Vue、Twilight、Dragonfly、PostgreSQL、ScyllaDB、RustFS を使用しています。それぞれがシステム内で明確な役割を担います。
プログラミング言語
Rust は、Discord ボット、バックエンド、カードエディター、Fjell グラフィックエンジンの基盤です。
Web フレームワーク
Web インターフェースの一部には Leptos を使用しています。Rust をブラウザ側でも使えるため、ほかの技術スタックとも自然につながります。
プログラミング言語
公開サイトのインタラクティブなページには TypeScript を使用しています。型チェックにより、コンポーネントと状態の一貫性を保てます。
Web フレームワーク
Vue は公開サイトのナビゲーション、ドキュメント、その他の操作部分をコンポーネントとして構成します。
ロゴ © Evan You. CC BY-NC-SA 4.0 · 変更なし
Discord 用ライブラリ
Discord との接続には Twilight を使用しています。このライブラリが、Rust で書かれたボットと Discord をつなぎます。
Fezhoof によるロゴ。CC BY-SA 4.0 · 変更なし
データベース
Dragonfly は Discord イベント、シャードと複数のゲートウェイ接続の調整、AutoMod、アクティビティログに使用する共有の一時データストアです。
ソフトウェアは追加の使用許諾付き BSL 1.1。
リレーショナルデータベース
PostgreSQL は、リレーショナルなデータや構造を扱うためのデータ層を補完します。ScyllaDB と並ぶ独立したインフラ構成要素です。
データベース
ScyllaDB は PostgreSQL と並んでデータ層を構成しています。Fjord 内では、それぞれ異なる用途に使い分けています。
オブジェクトストレージ
S3 互換のオブジェクトストレージとして RustFS を使用しています。PostgreSQL と ScyllaDB に加えて、独立したストレージ層を構成します。
ソフトウェア Apache 2.0. RustFS は RustFS, Inc. の商標です。