テクノロジー

数万QPSをさばくサーバーを障害件数0件で刷新した件 〜 アプリ用SSPサーバー言語刷新時の工夫の紹介

こんにちは。Yahoo!広告 ディスプレイ広告エンジニアの小杉です。
アプリへの広告配信を行うシステムの開発を担当しています。

昨年、数万 QPS のリクエストを受けるレガシーなアプリ用 SSP(Supply Side Platform)サーバーを Go に言語刷新し、 障害件数 0件で完了できました。

この記事では、これほど大規模なシステムリニューアルを無事故で完遂するためには、
どのような点に注意しプロジェクトを進行したのかご紹介します。

なぜ言語を刷新したのか

このシステムは旧来 PHP で作られていましたが、それを今回 Go 言語に置き換えました。

ヤフーでは、レガシーなシステムをモダン化して開発しやすい環境を作ることに取り組んでいます。
そのため、モダンな言語への刷新とシステムの仕様の見直しを行いました。これにより、アプリ用 SSP は開発コストの減少というメリットが得られました。

スマホアプリでの広告配信の仕組み

Yahoo!広告 ディスプレイ広告は、さまざまなサービスや提携パートナーサイトに広告を配信しています。
例えば、Yahoo! JAPAN アプリを利用したときに画面の上部に広告が表示される場合があると思います。

広告の例

このとき、Yahoo! JAPAN アプリは広告 SDK を用いて広告取得リクエストを行っています。

スマホアプリで広告を配信する場合、広告 SDK を用いてアプリ用の SSP に広告取得リクエストを行い、SSP から得られたレスポンスを用いてアプリ上で広告の描画を行います。

処理フローはこのようになります。

  1. アプリが広告 SDK を用いて SSP へ広告取得リクエストを送信
  2. SSP が DSP(Demand Side Platform) に広告取得リクエストを送信
  3. DSP が広告情報を SSP に返却
  4. SSP が SDK に広告情報を取得形式に応じた広告レスポンスを返却
  5. アプリが SSP から得られたレスポンスを用いて広告表示

処理フローの図

このとき、広告の取得形式は下記の 3つに分類されます。

  • HTML 形式(banner 形式)
    • SSP は DSP から得られた広告情報を基に、 HTML を生成してレスポンスを返却します。
  • JSON 形式(native 形式)
    • SSP は DSP から得られた広告情報を基に、広告の要素が入った JSON を生成してレスポンスを返却します。
      例えば、「返却した広告の種別」「表示させる画像」「遷移先 URL」などの要素を返却しています。
  • 動画形式(VAST 形式)
    • SSP は DSP から得られた広告情報を基に、 IAB で定義されている VAST を返却します。
      これをもちいることで、動画メディアの視聴時に動画広告を挿入できるようになります。

SSP では、下記の要素の組み合わせから非常に多くの配信パターン(合計で 1,000 パターン以上)が存在します。

  • OS
  • アプリ
  • 広告 SDK の version
  • 広告の取得形式
  • 広告の種別

そのためシステムに手をいれる際は、あらゆる組み合わせパターンで差分が生じないよう、慎重に実装を行う必要があります。

過去の失敗を基に、今回の実施方針を決定

SSP では、数年前に Node.js に言語刷新しようと取り組んでいました。
しかしその時は、大きく分けてこの2つの要因で、言語刷新を断念することになりました。

  • 仕様書とコードの差分があった
    • 約10年前に構築されたシステムであり、どこかのタイミングで仕様書とコードで差分が発生していた
    • そのため、新・旧システムで異なるレスポンスを返却してしまう不具合が起きた
  • 言語が変わったことにより、予想外の性能劣化が起きてしまった
    • XML に対して XSLT を用いて HTML を生成している部分で、変換部分での性能の劣化が大きく、CPU が高負荷な状態に
    • 結果、本番リリース寸前で SSP で想定している最大 QPS のリクエストを処理しきれないことが明らかになった

これらを教訓として、失敗を繰り返さないよう、以下の方針を決めました。

  • 言語刷新に取り組む前に、仕様書とコードの差分を適切に整理する
  • 既存のシステムと同じリクエストを受けたとき、同じレスポンスを返却できるかテストを行う
  • 開発段階で繰り返し実施しやすいように、性能試験の自動化に取り組む

注意したこと・行ったことを紹介していきたいと思います。

【1】取り組む前に仕様の棚卸をする(CI/CD も整備)

言語刷新に取り組む前に現状の仕様の把握とともに、仕様と実装で差分が発生していないかチェックを行いました。

差分がある場合には、コードを正として仕様書で差分が生じている部分を修正し、仕様があいまいな場合は関係者にヒアリングして何が正しい仕様であるか決定して整合性を取るようにしました。また、すでに使われなくなった機能を精査して、不要な機能を削除しました。

CI/CD の自動テストは、SSP が SDK に返却するレスポンスが想定通りか確認するテストを自動で行うもので、GitHub で PR を出す・コミットが push されたときに毎回自動テストが実行されるようになっています。
SSP が想定している SDK からのリクエストおよび DSP のレスポンスを用意し、固定の値を用います。下の図の赤い部分が固定の値を設定している部分です。

自動テストの図

このとき、自動テストで確認する事項としては SSP が生成する広告リクエスト・レスポンスなどをSSP が生成する要素について想定通りの値がセットされているかなど確認しています。

それらの値が完全に一致するかチェックすることでデグレードが発生していないか確認します。

旧 SSP(PHP 版)と 新 SSP(Go 版)のどちらも SDK・DSP との I/F に差は無いはずです。
旧 SSP で使用していた自動テストの内容をそのまま使うことで 新 SSP の実装漏れ・ミスがあった場合、自動テストが通らなくなります。これにより、デグレードが発生していないかテストするのが非常に容易となりました。

【2】実際のリクエスト・レスポンスを用いて、リプレイテストを行う

ただし、自動テストのテストケースではすべてを網羅し切れているとは言えません。
実際のリクエストと違って想定通りの都合の良いデータを用いてテストを行っているため、
自動テストが全て通ったからといって、差分がないと言い切れません。

そのため、実際の広告取得のリクエスト・レスポンスを用いてテストを行うリプレイテストを実施することで上記の懸念点の解消を図ります。

リプレイテストは、実際に稼働している 旧 SSP のリクエストを 1,000 件程度サンプリング抽出・取得し、新 SSP でも同一リクエストをします。そして同様のレスポンスを返却しているか確認します。

リプレイテストで保存する情報

  • SDK のリクエスト
  • SDK へのレスポンス
  • 広告の配信設定
  • DSP から返却される広告レスポンス

リプレイテストを実施して新・旧レスポンスで差分が発生した場合には、何が原因で差分が生じているのか、その差分は許容されるものなのかといった部分を精査します。
バグと判断された場合はコードを修正して改めてリプレイテストを行うということを繰り返しました。
これにより、機能面で問題ないことを確認できました。

【3】CI で性能試験も行い、言語違いによる性能劣化を早期検知する

広告配信において、性能の劣化でレイテンシが悪化するとアプリの描画が遅くなるなどといった悪影響があるため、言語刷新では性能の劣化が許容できません。

性能試験では CI ツールから実行できるようにして、レイテンシなどのメトリクスやプロファイルを自動で取得できるようにしたことで性能改善 → 性能試験のサイクルを回しやすくしました。

当初は新・旧 SSP と比較するとレイテンシが悪いことが判明したため、性能改善に取り組むことにしました。結果、旧 SSP と同等のレイテンシまで改善できました。
こちらの改善は1~2週間程度という短い期間で行えましたが、性能改善 → 性能試験のサイクルがうまく回ったことが大きな要因となっています。

以上の取り組みを行うことで、機能面・性能面で問題がないことをしっかりと確認できたので、
いよいよ本番リリースへとコマを進めることになりました。

【4】段階的リリースと、KPI の監視で問題発生を検知する

このプロジェクトは 2021年4月から始まり、2021年10月 ついに本番へのリリースが始まりました。

自動テスト・リプレイテスト・性能試験で問題ないことは確認しているものの、いきなり 新 SSP に全て切り替えることは何か問題が起きたときにはリスクが高いです。
そこで 1%、10%、50%、100% のように 新 SSP のリクエスト比率を徐々に増やしていく段階リリースを行うことにしました。

段階リリースを行うにあたり、途中で何かしらの問題が発生したことを検知するために新・旧の SSP の指標を比較して、問題ないことを確認します。

  • 新・旧 SSP で、レスポンスに差分がなければ
    • レスポンスが一緒なので、広告の表示に影響を与えていないはず
  • 新・旧 SSP で、広告のクリック率(CTR)や広告の viewable 率に変化がなければ
    • 広告表示後の広告タップなどの動作に影響ない、つまり広告の表示に影響を与えていないはず

この考えを基に 新・旧 SSP のそれぞれのログと DSP のログを突き合わせることで CTR・viewable 率の指標を算出しました。もし、新 SSP で不具合があれば CTR・viewable 率の指標が旧 SSP と異なる数値となるので、それらの指標を比較するようにしました。
段階リリースの度に新・旧の SSP の間で指標が大きく相違していないことを確認して移行を進めていきました。

まとめ

こうして、100% リリース後も指標は大きく相違せずに、SSP では、トラブル 0件でリリースが完了しました。
トラブルを発生させないための取り組みについて紹介してきましたが、要点をまとめます。

  • コードと仕様書の整合性を取り、不要な機能を持ち込まないような事前準備をしっかりと行う
  • テストを自動化して、テストを容易に繰り返せる状態にして開発を進める
  • リリースは段階を踏んで新・旧の指標を比べて問題ないことを確認して進める

本記事がシステム開発時に発生する障害を減少させるための一つの方法として参考になればと思います。

こちらの記事はいかがでしたか?

  • 学びがある
  • わかりやすい
  • 新しい視点

ご感想ありがとうございました


小杉 将史
バックエンドエンジニア
広告配信システムの開発と運用を担当しています。中型・大型二輪の免許を取得しました。

関連記事

このページの先頭へ