7
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

A/BテストしてるWebサービスにfastlyを導入した

Last updated at Posted at 2018-08-31

概要

A/Bテストしてるサイトにfastly導入したのでやったことをまとめておく。
基本的にはfastly公式に書かれている内容を踏襲して設定すれば問題ない。

TL;DR
SP/PC向けのA/Bテストするサービスにfastlyを使うときは、VCLを書く。

  • リクエストのユーザエージェントを見てヘッダに載せる
  • A/Bパターンを計算してヘッダに載せてset-cookieする
  • UA+ABパターンの組み合わせをVaryヘッダに載せる

背景

  • バックエンドにAPIサーバを置きBFFでSSRしてhtmlを配信する、SPAのWebサービスを開発中。
  • 検索されたクエリでバックエンドからデータを取得してリストで表示する。
  • このWebサービスのエッジにhtmlをキャッシュするためにfastlyを導入する。

課題1

ユーザエージェントを見てPCとSPで表示するビューコンポーネントを変えている。しかし、ページ対して最初にアクセスしてきたユーザのユーザエージェント(SP/PC)のhtmlがキャッシュされてしまい、次回以降にアクセスするユーザのユーザエージェントがSP/PCに関わらず、最初にアクセスしてきたユーザ向けに生成されたhtmlが返されてしまう。

例えば、

  1. ユーザ1(SPユーザ)が/listページにアクセスする
  2. BFFはユーザエージェントを確認しSP向けのhtmlをSSRして返す
  3. fastlyが/listのSP向けhtmlをキャッシュする
  4. ユーザ2(PCユーザ)が/listページにアクセスする
  5. fastlyはSP向けのhtmlキャッシュを返す

やったこと1

fastlyにも書かれているが、ユーザエージェントからsp/pcを判定するvclを書いてcustom vclに登録し、Mainから呼び出せばよい。このVCLをほとんどコピペで問題ない。sp/pcだけ分かれば良いならreq.http.X-UA-Deviceにはsp/pcを書くようにする。

detect_device.vcl
sub detect_device {
  set req.http.X-UA-Device = "pc"
  if (req.http.User-Agent ~ "(?i)ip(hone|od)" || ...) {
    set req.http.X-UA-Device = "sp"
  }
}

これをメインのVCL(main.vcl)でincludeし、fastlyへリクエストが届いたとき(vcl_recv)にメソッドを呼び出してVaryヘッダに載せる。

main.vcl
include "detect_device"

sub vcl_recv {
#FASTLY recv
  ...

  call detect_device;
  return(lookup);
}

sub vcl_fetch {
#FASTLY fetch
  ...

  if (beresp.http.Vary) {
  	set beresp.http.Vary = beresp.http.Vary ", X-UA-Device"; 
  } else {
  	set beresp.http.Vary = "X-UA-Device";
  }
  return(deliver);
}

fastlyではVaryヘッダの値でキャッシュが紐づいて保管されるので、X-UA-Deviceが別なら異なるキャッシュとして管理されるようになる。これでSP向け、PC向けに分けてキャッシュを管理できる。
BFFではX-UA-Deviceの値を取得し、それに応じてSSRしたhtmlを返す。

参考:
https://www.fastly.com/blog/best-practices-using-vary-header
https://docs.fastly.com/guides/vcl-tutorials/delivering-different-content-to-different-devices

課題2

検索パターンをA/Bテストするためにcookieで振り分けている。しかし、ユーザエージェントの場合と同様に一度パターンがキャッシュされてしまうと、それ以降全てのユーザに対して同一パターンのキャッシュが返されてしまう。

例えば、

  1. ユーザ1が/listページにアクセスする
  2. BFFはユーザ1にランダムにAパターンを割り振りcookieをセットする
  3. BFFはAパターンのhtmlをSSRして返す
  4. fastlyが/listに対してAパターンのhtmlをキャッシュする
  5. ユーザ2が/listページにアクセスする(A/Bパターンが振り分けされない)
  6. fastlyはAパターンのhtmlキャッシュを返す

やったこと2

このケースでもVCLを用意することで解決する。具体的には、BFFでA/Bパターンを計算してset-cookieするのではなく、fastlyのVCLでA/Bパターンを割り振りset-cookieする。

abtest.vcl
sub set_abparam_into_header {
  #in recv
  if (!req.http.Cookie:TestParam) {
    if (randombool(50,100)) {
      set req.http.x-test-param = "a-pattern";
    } else {
      set req.http.x-test-param = "b-pattern";
    }
  } else {
    set req.http.x-test-param = req.http.Cookie:TestParam; 
  }
}

sub set_vary_header {
#in fetch
  if (beresp.http.Vary) {
  	set beresp.http.Vary = beresp.http.Vary ", x-test-param, X-UA-Device"; 
  } else {
  	set beresp.http.Vary = "x-test-param, X-UA-Device";
  }
}

sub set_abparam_into_cookie {
#in deliver
  if (!req.http.Cookie:TestParam){
    add resp.http.Set-Cookie = "TestParam=" req.http.x-test-param + "; Expires=" + time.add(now, 24w) + "; Path=/;";
  }
}

それぞれのメソッドをmain.vclの、vcl_recv, vcl_fetch, vcl_deliverから呼び出す。
これにより、A/Bテストのパラメータがリクエストのヘッダに載り、クライアントへset-cookieして返される。
BFFでは、リクエストのヘッダからx-test-paramを取得して、適切なパターンのhtmlをSSRする。

参考:
https://www.fastly.com/blog/ab-testing-edge

おまけ: はまったこと

恥ずかしながらちょっとはまったところをメモしておく。fastly関係なくね?と言われたら何も言えない...

  1. Vary headerは、対象のheaderのNameを書くので、""で囲まないといけない
  2. ヘッダのNameはCase Insensitiveということを忘れててBFFでreq.headers["X-UA-Device"]とかで読み取ろうとしてundefinedになった
  3. set-cookieでPath=/;を忘れてパスごとにcookieが設定されてしまった。

などなど...

まとめ

SP/PC向けのA/Bテストするサービスにfastlyを使うときは、VCLを書く。

  • リクエストのユーザエージェントを見てヘッダに載せる
  • A/Bパターンを計算してヘッダに載せてset-cookieする
  • UA+ABパターンの組み合わせをVaryヘッダに載せる

VCL怖くない!
実際に動くのかの確認はactiveにしてサーバあげないとできないけど、
saveしたらエラー教えてくれるし、バージョン管理もできるし、結構よくできてる。

余裕があったら、Drone CIでfastly向けブランチをpushするたびにfastlyのエンドポイントを構築・更新する仕組みも作っているので、記事にするかも。

7
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?