ペンギンテスト
システム開発におけるペンギンテストについて、技術的に掘り下げて解説したいと思います。
ペンギンテストは日本の医療系スタートアップ Ubie(ユビー)社の開発チームが提唱した造語で、一般的な技術用語としては 「シャドウテスト(Shadow Testing)」や 「トラフィックミラーリング(Traffic Mirroring)」と呼ばれる手法に該当するようです。
特に大規模なリファクタリングやレガシーシステムからのリプレイスを行う際に、バグをゼロにするための切り札として注目されているとのこと。
ペンギンテスト(シャドウテスト)のアーキテクチャ
このテストの肝は、ユーザーには古いシステムの正解データを返しつつ、裏側で新しいシステムにも同じ仕事をさせ、答え合わせをするという点です。
処理フローのイメージ
- リクエスト受信: ユーザーからのリクエスト(HTTPリクエスト等)を受け取る
- 分岐(Mirroring): リクエストを複製し、「現行システム(Old)」と「新システム(New)」の両方に投げる
- 実行: 両方のシステムが処理を行う
-
比較(Diff): 両者のレスポンス(計算結果やJSONのボディなど)を比較する
- 一致: 成功ログを出力
- 不一致: 失敗ログ(Diff内容)を出力してアラートを出す
- レスポンス: 「現行システム」のレスポンスのみをユーザーに返す。(新システムのレスポンスは破棄する)
なぜこのテストが強力なのか?
従来の単体テストや結合テストでは拾いきれないバグを検出できる点が最大のメリットです
-
本番データの「予期せぬ入力」を網羅できる:
開発者が想定したテストデータではなく、ユーザーが実際に入力する「汚いデータ」「エッジケース」ですべてテストできます -
ユーザー影響がゼロ:
新システムがどれだけエラーを吐いても、ユーザーに返されるのは「現行システム」の結果なので、事故になりません -
パフォーマンス検証も兼ねる:
本番と同じ流量のトラフィックを新システムに浴びせることとなるため、リリース前に負荷試験(ロードテスト)が完了している状態になります
具体的な実装パターン(技術スタック別)
エンジニアの方が実装を検討する場合、主に以下の3つのレイヤーで実現可能です
A. アプリケーション層(コードレベル)で分岐
最も手軽な方法です。コントローラーやAPIゲートウェイのコード内で分岐させます。
- メリット: 複雑なインフラ構築が不要。特定のAPIだけテストしたい場合に有効
- デメリット: アプリケーションのコードが汚れる
-
ツール: 自前実装、または
GitHub Scientist(Ruby発祥のライブラリで、各言語に移植版あり)などが有名
B. ミドルウェア/プロキシ層で分岐
Nginxやロードバランサーの設定で行います
-
手法:
Nginxのpost_actionやmirrorディレクティブを使用 -
ツール:
GoReplay(Go製のツールで、トラフィックをキャプチャして別サーバーに再送できる) がよく使われる
C. インフラ層(クラウド・Service Mesh)で分岐
Kubernetesなどを利用している場合のモダンな手法です。
- AWS: VPC Traffic Mirroring を使用してパケットレベルで複製
-
Kubernetes (Istio):
VirtualServiceのmirror設定を使うことで、コードを一行も書き換えずにトラフィックを複製できる
最大の課題:副作用(DB更新)の制御
ペンギンテストを実装する際、もっとも注意が必要なのが「書き込み系(更新系)の処理」です
新システムが本番DBに対してINSERTやUPDATEを発行してしまうと、データが二重登録されたり、データ整合性が壊れたりします
【回避策】
-
参照系(GET)のみ実施する:
最も安全。計算ロジックや検索機能のリプレイスならこれで十分 -
モック/スタブに差し替える:
新システムのDB接続先を、書き込みを行わないモック等のドライバに差し替える -
トランザクションのロールバック:
新システムの処理の最後で必ずROLLBACKを行い、DBへのコミットを防ぐ(ただし、外部APIコールなどの副作用は防げないため注意が必要)
まとめ
「ペンギンテスト」とは、「本番環境という最も過酷な海に、ユーザーを守りながら新システムを飛び込ませる」テスト手法です。こういう手法もあるんだなということを覚えておきたいと思います。