← 記事 個人開発 · ケーススタディ

「緯書」八字命盤演算機能修復技術レポート

技術レポート:八字命盤ツールのWASMロード失敗問題を修復。Cloudflare CDN圧縮競合の調査とエッジコンピューティングでの解凍対策を解説。

1. 問題の背景

「緯書」ブログの八字命盤ツール(WASM版)を統一的な「帛書」ビジュアルスタイルにリファクタリングし、Cloudflare Workersへデプロイした後、ユーザーからフロントエンドページで「推算命盤」をクリックするとエラーが発生するとの報告が寄せられた:演算失敗: WASMファイルを読み込めません。同時に、コンソールにはマジックナンバー不一致エラーが出力されていた:expected magic word 00 61 73 m, found cf ff ff 7f

この問題により、核となる八字排盤機能が完全に動作しなくなった。本レポートでは、この問題の調査プロセス、根本原因分析、そして最終的な修正ソリューションについて詳細に記録する。


2. 障害診断と原因分析

フロントエンドのロードロジック、ネットワークリクエストのパケットキャプチャ、WebAssemblyインスタンス化プロセスを深く調査した結果、この障害は単一の原因によるものではなく、3つの独立した技術的問題が重なって発生したことが判明した。

2.1 圧縮形式の競合とCDNによるインターセプト(核心問題)

現象: フロントエンドはfetchganzhi.wasm.gz(18MB)を取得し、DecompressionStream('gzip')で解凍しようとした。しかし、解凍後にWebAssembly.instantiateへ渡されたバイトストリームの先頭8バイトはcf ff ff 7fであり、標準的なWASMマジックナンバー00 61 73 6dではなかった。

分析: cf ff ff 7fはBrotli圧縮アルゴリズムのファイルヘッダー識別子である。さらにパケットキャプチャを行ったところ、フロントエンドは.gzファイルをリクエストしていたにもかかわらず、ブラウザがリクエスト時に自動的にAccept-Encoding: gzip, deflate, brヘッダーを付与していたため、Cloudflare CDNのエッジノードがクライアントのBrotli対応を検知し、元のGzipファイルをさらにBrotliで再圧縮し、Content-Encoding: brを返していた。

その結果、フロントエンドのDecompressionStream('gzip')はBrotli形式のデータを受信し、解凍に失敗。WebAssemblyエンジンがマジックナンバー不一致の致命的エラーを投げることになった。

2.2 JSONデータ解析パスの異常

現象: WASMロード問題を解決した後、フロントエンドのコンソールには計算完了と表示されたが、ページ上の四柱、五行、大運などのUI要素は依然としてプレースホルダー「—」のままだった。

分析: 元のJSグルーコードは、WASMが返すJSON文字列を処理する際に、data.pillarsなどのトップレベルプロパティへ直接アクセスしていた。しかし、最新版にコンパイルされたWASMモジュールが返すデータ構造はresultオブジェクトにラップされており、実際の構造は{"result": {"pillars": {...}}, "success": true}だった。パスが誤っていたため、フロントエンドのレンダリング関数はundefinedを受信し、UIが更新できなかった。

2.3 フィールド形式の非互換

現象: 四柱の干支データが対応するDOMノードに正しく反映されなかった。

分析: 旧バージョンのレンダリングロジックは、WASMが天干と地支を分離したフィールド(例:yearStem: "庚", yearBranch: "午")で返すことを想定していた。しかし実際に返されたデータは、干支を結合した単一の文字列(例:year: "庚午")だった。そのため、フロントエンドの分割代入が失敗した。


3. 修正ソリューションと実装

上記の3つの問題に対し、「サーバーサイド解凍+フロントエンド適応」という複合的な修正戦略を採用した。

3.1 エッジコンピューティング層(Cloudflare Workers)の再構築

ブラウザとCDN間の自動圧縮コンテンツネゴシエーションの競合を完全に回避するため、フロントエンドでのDecompressionStreamによる解凍方式を断念し、Cloudflare Workersのエッジコンピューティング機能を活用することにした。

具体的な実装: インターセプトスクリプトworker.jsを作成し、/dist/ganzhi.wasmへのリクエストを検知すると、Workerがサーバーサイドで静的アセット内のganzhi.wasm.gzを読み取り、エッジノードでGzip解凍を完了させ、Content-Type: application/wasmを強制設定して、純粋な生のWASMバイトストリームをそのままフロントエンドへ返すようにした。

このソリューションは二重圧縮の競合を解決しただけでなく、エッジノードの計算能力を活用し、クライアント側ブラウザの解凍負担も軽減した。

3.2 フロントエンドレンダリングロジックの修正

JSON構造とフィールド形式の変更に対応するため、index.html内のrenderResult関数を書き直した。

修正項目旧ロジック新ロジック
データ階層const p = data.pillars;const data = raw.result ? raw.result : raw; const p = data.pillars;
四柱の分割[p.yearStem, p.yearBranch]const sb = p.year; $(sId).textContent = sb[0]; $(bId).textContent = sb.slice(1);
五行レンダリング特定の中国語文字キーに依存中英辞書マッピングを追加し、対応するCSSカラークラスを持つHTMLタグを動的に生成

3.3 ビジュアルスタイルの統一

機能修正と同時に、八字命盤のUIスタイルを「緯書」ブログのメインサイトと深く統一した。白紙背景色(#FAF7F2)、朱砂色のアクセントカラー(#C0392B)、墨色の文字色(#1A1A1A)を導入し、上部には統一された戻るナビゲーションバーを追加。ブランドビジュアルの一貫性を確保した。


4. まとめ

今回の修正により、Cloudflare CDNの自動圧縮メカニズムとWebAssemblyロード間の競合問題を解決することに成功した。解凍ロジックをWorkersのエッジノードに前倒しし、フロントエンドのデータ解析パスを再構築したことで、八字命盤ツールは安定稼働を回復した。

この事例は、今後のServerlessアーキテクチャでの大規模WASMファイル展開において貴重な教訓を提供している:非標準的なバイナリアセットを扱う際は、CDNのデフォルトの透過的圧縮動作に注意し、必要に応じてカスタムWorkerスクリプトによる明示的な介入を行うべきである。