1
/
5

小話!推薦基盤チームを支えるスクリプトたち

Photo by Markus Winkler on Unsplash

本記事は「Wantedly 新卒 Advent Calendar 2021」の6日目の記事です。

こんばんは! Wantedly Visit の推薦基盤チームのnasaです。

今回は推薦基盤チームの開発に役立っているスクリプトの話をしようと思います。
ツールや仕組みの話についてはたくさんブログが書かれているので、今回はスクリプトのみに絞って話します。書いていて気づいたのですが、そこまでスクリプトの量がなかったので小話になります。

コーヒー片手にかる~く読んでください

Redisのデータの中身を比較するスクリプト

最初に紹介するのは、Redisのデータに差分があるかを確認するクリプトです。なぜこのスクリプトが欲しいのか?という背景を最初に説明して、スクリプト本体の説明をしようと思います。

背景

Wantedly Visitの検索・推薦に用いるデータのほとんどはGoogle Cloud BigQueryに保存されています。
Visitの検索を扱うマイクロサービスでは、これらのBigQuery上にある検索・推薦に必要なデータをRedisに同期しておき、ユーザーのリクエスト時にはRedisの情報を見てレスポンスを返しています。

なぜ、Redisにデータを同期しておくかというとユーザーの検索リクエスト時に毎回BigQueryに問い合わせるのは遅いのでRedisに同期しておき、高速にレスポンスを返せるようにしています。
この同期はcron jobで定期実行しています。


一時期このcron jobの動作を変更せず(Redisに書き込むデータが変わっていなければ良い)コードをリファクタリングするということをやっていたのですが、BigQueryからデータを取得するSQLの変更や、Redisにデータを書き込んでいる箇所の変更など重要なロジックの変更を含んでいました。これらの動作確認を行う際に、「本当に既存のジョブとRedisに書き込んでいるKeyやデータが変わらないのか?」という疑問を拭いきれなかったので差分を見るためのスクリプトを書きました。

スクリプト

こちらがその時に書いたスクリプトとなります。

現在はこのスクリプトはそのままは使っておらず、k8s podにRedisを立ててそのpod内でジョブを実行するなどしています。社内ツールのkubeを使っていたりするので、今回は前使っていた方の紹介をしようと思います。

このスクリプトでは次のことをやっています。

  1. Redisのクリーンアップ (既存のデータに影響されたくないので)
  2. 変更後のブランチのジョブを実行
  3. rdbコマンドを使いRedisのデータを書き出す
  4. 手順2,3を変更前のブランチでも行う
  5. 書き出したファイルの差分を取る
#!/bin/sh

# HOW TO USE
# ジョブ名と2つのブランチ名を入力することで、それぞれのブランチでredisに何かしらの書き込みを行うjobの実行結果に差分がないかを調べてくれる君です。
#
# 1. redis-server redisをlocalで立てる。
# 2. dump.rdb解析ツールを入れておく https://github.com/sripathikrishnan/redis-rdb-tools
# 3. kube sb|qa fork
# 4. ./script/compare <jobname> <branch1> <branch2>

compare() {
  FILE1=$1
  FILE2=$2

  # rdbコマンドでdump.rdbを解析している
  # diffコマンドはdiffを取るのに優れた出力をしているらしい
  # -x をつけておくとexpireを無視して解析してくれる
  HASH1=$(rdb --command diff -x $FILE1 | sort | md5)
  HASH2=$(rdb --command diff -x $FILE2 | sort | md5)

  if [[ $HASH1 == $HASH2 ]]; then
    echo "!!!!! eq !!!!!"
  else
    echo "!!!!! neq !!!!!"
  fi
}

compare_job_result() {
  JOBNAME=$1

  COMMIT_HASH1=$2
  COMMIT_HASH2=$3

  export REDIS_SERVER_HOST=localhost:6379

  git checkout $COMMIT_HASH1
  redis-cli flushall
  go run jobs/scout/$JOBNAME/run.go # ジョブが置かれているディレクトリは決まっていたのでファイルパスではなくジョブ名をとっている
  redis-cli --rdb dump/${JOBNAME}_${COMMIT_HASH1}.rdb

  git stash
  git checkout $COMMIT_HASH2
  redis-cli flushall
  go run jobs/scout/$JOBNAME/run.go
  redis-cli --rdb dump/${JOBNAME}_${COMMIT_HASH2}.rdb

  echo "check db diff"
  compare dump/${JOBNAME}_${COMMIT_HASH1}.rdb dump/${JOBNAME}_${COMMIT_HASH2}.rdb
}

compare_job_result $1 $2 $3

redisのデータ比較はredis-rdb-toolsを使っています。https://github.com/sripathikrishnan/redis-rdb-tools
expireはnsだかms単位で更新されていて容易にずれてしまいます。rdb --command diffでは-xオプションを付けることでexpireを省いた情報を出力してくれます。このおかげで必要は範囲で比較を行うことが出来ました。

このスクリプトのおかげで動作を変更せずにジョブをリファクタリングしやすくなりました。
書き捨てのスクリプトだったので課題も多く、localのRedisに書き込むことや、ジョブが直列で走るのでジョブの実行時間が長いと辛いことがあります。

これらは日々いい感じにしながら開発を行っている状態です。

本番のリクエストをお手軽に再現する

次のスクリプトの紹介です。
お次は本番のリクエストを再現するためのスクリプトです。非常に短くパッ書けるのですが、非常に僕の助けになっているものです。

このスクリプトは次のことを行っています。

  1. まず事前に本番のリクエストパラメーターを手元に持ってきています (BigQueryにログが吐かれているのでそれをjson形式で取得している。これはスクリプト内ではやっておらず手動でやっています)
  2. リクエストパラメーターのjsonをいい感じにパースする (ダブルクオーテーションなどをいい感じにしている)
  3. xargsとgiro(社内で使っているgRPCリクエスト送信ツール)を用いてgRPCリクエストを送信する
#!/bin/bash

# SELECT params
# FROM `access_log`
# WHERE accessed_at BETWEEN "2021-11-05 10:00:00.000000 UTC" AND "2021-11-05 12:00:00 UTC"
# AND grpc_method = "/wantedly.visit.recommendation_project.ProjectService/ListProject"

cat params | \
  jq '.[].params' | \ # paramsというフィールドにリクエストパラメーターが入っている
  sed 's/\\"/\"/' | sed 's/^"//' | sed 's/"$//' | sed 's/"/\\"/' | \ # jsonを整形している
  xargs -S 5000 -t -P10 -IX giro call wantedly.visit.recommendation_xxx.xxxService/Listxxx --rpc-server=xxx.xxx:80 X

このスクリプトを使う場面としては2つです。

  1. 負荷計測を行いたいとき
  2. 動作に差分がないかを比べたいとき

xargsは-Pオプションで並列度を調整することが出来ます。これを用いで、並列に本番のリクエストを送信することで負荷計測を行っています。最近高速化を行うプロジェクトに多く取り組んでいるため、このスクリプトによる計測がかなり役立っています。(レスポンスタイムなどはnewrelicで計測している)

本番のリクエストを大量に、変更後、変更前の両方に送信して意図しない変更がないかを確認するときにも役立ちます。比較自体はこのスクリプトでは行っていないのですが似たものを使い、本番のリクエストに対するレスポンスに差分がないかを見ることで実際にユーザーが送信したパラメーターを使ってテストすることが出来ます。これはリリースのたびに動かしているのではなく、大規模な内部設計の変更を安全にリリースするために動かしました

ここで一つ小話ですが、xargsは引数のサイズに制限があり巨大なjsonパラメーターをxargs経由で送信しようとするとエラーになってしまいます。このサイズ制限は-Sオプションで変更することができ、今回のスクリプトだと5000bytesに設定しています。(デフォルトだと255bytes)

まとめ

今回は推薦基盤チームを支えるスクリプトの紹介をしました。
どちらのスクリプトも改善の余地があるものの人の手でやるよりも爆発的に開発しやすくなっています。

Wantedly, Inc.からお誘い
この話題に共感したら、メンバーと話してみませんか?
Wantedly, Inc.では一緒に働く仲間を募集しています
4 いいね!
4 いいね!

今週のランキング

近藤 アサンさんにいいねを伝えよう
近藤 アサンさんや会社があなたに興味を持つかも