ishikawa_pro's memorandum

若手webエンジニアの備忘録です.

Swift Node.js Docker AWS etc...色々やります。

「入門 監視」を読んだ

こんにちは。
「入門 監視」という技術書を読んだので、その記録です。

読んだモチベーションとしては、普段サーバーサイドエンジニアをしていて、業務の中でアプリケーションやインフラなどでアラートが飛んできた時に色々対応したり見たりすることも多いですが、雰囲気で数値などをみてたり、今までの経験から対応してしまってることも多く、ちゃんと監視に関する知識がないなと思ったのが1つです。
あとは、最近関わってるプロジェクトに Datadog APM を入れて、いろいろアプリケーション内の数値なども見れるようになったけど、もっとこの情報をうまく活用できるようになりたいなと思ったのもきっかけの1つです。

感想

A5サイズの本で、ページも200ページくらいなのでサクッと読めました。
内容も入門というだけあって分かりやすかったです。
ツールの使い方とかはほぼ載ってなくて、いろいろなレイヤーごとに何を監視すべきかなどが分かりやすく解説してありました。

構成は、

  • 1章 監視のアンチパターン
  • 2章 監視のデザインパターン
  • 3章 アラート、オンコール、インシデント管理
  • 4章 統計入門
  • 5章 ビジネスを監視する
  • 6章 フロントエンド監視
  • 7章 アプリケーション監視
  • 8章 サーバ監視
  • 9章 ネットワーク監視
  • 10章 セキュリティ監視
  • 11章 監視アセスメントの実行

という感じです。
前半は、監視とはどういうことをするものなのかや、どうやって監視をしていくかなどについて書いてありました。
後半は、ビジネスやアプリケーション、サーバーなどいろいろなレイヤーでの監視の方法や取るべき数値などが説明してありました。 最後の方のネットワーク監視・セキュリティ監視は、僕に監視する以前にネットワークやlinuxに関する知識が足りなくて難しかったので、軽く読み流しました。
監視についてしっかりとした知識がなくて、これから勉強していこうという人にはぴったりな本だと思いました!
いい本だし、読むの早い人なら2日くらいで読めるので興味ある人はぜひ読んでみてください!

ISUCON11 予選に参加しました

こんにちは。
ブログ更新がすごく久しぶりになってしまいました。
ISUCON11 予選に参加したので、今年も出場レポートを残しておきます。

isucon.net

自分の実力とか成長を確かめるために、今年も1人チームでの参加にしました。

使った言語

普段業務では、Node.js や TypeScript でサーバーを書いてるので、Node.js (TypeScript) 実装で挑戦しました。
フレームワークは定番の Express.js だったので、特に心理的障壁などもなくコード自体は読むことができました。

やったこと

来年の自分用に、やったことをまとめておきます。
初期スコアをちゃんと記録してなかったのでかなり曖昧ですが、Node.js 実装だと 1000点 ちょっとくらいでした。
Go言語実装の方が、結構初期スコア高かったような気がしました。

リポジトリ作成など

ソースコード管理のため、GitHubのプライベートリポジトリにコードをpushしました。

github.com

コードの編集は、ローカル環境に clone して作業しました。
やったことを記録しておくために、一応作業内容単位でブランチを切って、プルリクエストを作成して進めました。
1人でやるので、CI/CD環境とかは作成してもそんなにうまみはないかなと思って特に構築しませんでした。

構成確認・ログ周りの設定

コードを読んだりする前に、アプリケーションの構成を確認してログの設定などをしました。
構成は、 Nginx + サーバーアプリ + MySQL(mariaDB) でした。
ログ周りの設定は、初めに Nginx のログフォーマットを修正してalp で解析できるようにしました。
github.com この作業は、事前に復習してて手元に作業手順を用意してたので、10分くらいで終わったと思います。
次に DB が MySQL だったので、slow query log を吐くように設定して、pt-query-digest で解析できるようにしました。

www.percona.com

これは手順書とか用意してなかったですが、15分くらいで終わったと思います。

isu_condition のINSERTを改善

alp でアクセスログを見ると、[post] /api/condition/:jia_isu_uuid のリクエストが多かったので、とりあえずそこのコードを確認しました。
1リクエストで複数の isu_condition をINSERTする際に、for文で1行ずつ INSERT をしていたので、1クエリで複数行INSERTできるようにSQLと実装を改善しました。

github.com

この改善で、スコアが 2809 まで上がりました。

isucondition テーブルにindexを貼る

次に確か、slow query log などを見てました。
isu_condtionを jia_isu_uuid で引いてるところがあり、EXPLAIN で実行計画を見たらテーブルスキャンしてそうだったので、インデックスを貼りました。
結果をちゃんとメモってなかったんですが、これは結構効果があり、スコアが9000前半くらいまでは上がりました。
確かここまででやって、13:30 くらいだったような。

isu_condtion の INSERTを改善(その2)

次にまた、 [post] /api/condition/:jia_isu_uuid の改善をしました。
レギュレーションによると、

POST /api/condition/:jia_isu_uuid で受け取ったコンディションの反映が遅れることをベンチマーカーは許容しています。

とのことだったので、 insert のクエリを投げた後に、 await で結果を待たないでレスポンスを返してしまうように実装を修正しました。
github.com これもそれなりに効果があり、スコアが 10204 になりました。
ここらへんまでで 14:00 過ぎくらいだったはずです。
順位は100位手前くらいだったような気がします。
お腹が空いたのでここら辺で一旦休憩しました。

DBの調整

去年の自分の参加ブログを確認して、thred_cache_size, query_cache_size, innodb_buffer_pool_size などを調整していたので、今回も値を調整してみました。
ISUCON10 予選に参加しました - ishikawa_pro's memorandum
これで、スコアが max 11000 くらいまで上がったはずです。(記録残してなかった。。)

その他もろもろ

前日の仕事の疲れが出てしまい、後半からかなり集中力が切れてしまいました。。

データの取得時にトランザクション貼ってるところが何箇所かあったので、一旦外してパフォーマンス見てもいいのではと、なんとなく思って、試したりとかしてました。
graph取得のトランザクション削除 by ishikawa-pro · Pull Request #3 · ishikawa-pro/isucon11-qualifying · GitHub
あんまり効果なかったです。

最後に isu の一覧取得で、 isuに紐付くisu_conditionのデータをfor 文を使ってシリアルに取得していたので、 Promise.all でパラレルに取得するようにしました。
github.com これもパフォーマンス的には変わらなかったです。
そもそも N+1 なので、SQLでJOINして1クエリで取得できるようにすべきだと思ったんですが、時間がなかったので無理でした。

最終的にDBのチューニングでは、値をいじりすぎたりして、ちょっとスコアを落としてしまい、9000点後半代でフィニッシュ。

振り返り

去年と比べると、自分が去年やれたことは、お昼過ぎくらいにはこなしててよかったです。ただ、そこからさらに改善するところまで持っていくことができなかったのが後悔です。
DB周りは、Indexを貼ったりなどをもっと積極的に試行錯誤してもよかったなと後になって思いました。
普段の業務でMongoDBを使ってて、SQLを全く書いてないのもあり、SQLの改善などに手を出せなかったので、なんとか来年までにSQLRDBも勉強する時間を作りたいなと思いました。
あと、いまだに3つのインスタンスを活かすところまで改善ができないので、そういうインフラとかの設定周りも勉強して伸ばしていきたいです。

来年もISUCONがあるかはまだ分からないですが、来年に向けて技術を高めていこうと思います!
運営や他の参加者の皆様、お疲れ様でした!!

vironでNature Remoのダッシュボードを作った

こんにちは。
最近は、iPad Airが届いたので、どう使おうか色々模索しながら遊んでいます!
このブログもiPad Airで書いてみてます!

今日はネタ記事です。
僕の部屋では、Nature Remoを使ってエアコンの操作やお掃除ロボの定時起動などをしています。
Natrure Remoは、デバイス自身が温度と湿度と照度のセンサーを備えていて、公開されているREST API 経由でその値を取得できるので、それを使って今回は遊んでみました。

やったこと

今回は、vironというswagger定義とサーバーサイドのコードを書けば、デザインは自動で作ってくれるマネージメントコンソールアプリのOSSを使って、Nature Remoのデバイス管理と1日の温度などをグラフ化してみました。
github.com

使った技術

vironは、いくつかのフロントへ返すレスポンスさえ規則に従っておけば、基本的には何の言語やフレームワークを使っても構わないので、軽くサーバーで使った言語やライブラリなどを紹介しておきます。

言語

  • TypeScript

今年から書き始めたから勉強も兼ねて。

フレームワーク

  • Express.js

使い慣れてるので。

DB

Herokuにデプロイしたかったから。

ORM

  • TypeORM

github.com
昔は業務でSequelizeを使ってたので(最近はmongoDB+mongoose)、Sequelizeにするか迷いましたが、TypeORMがいいらしいとの噂を耳にしたので、今回は使ったことのないTypeORMにしてみました。

デプロイ先

  • Heroku

サーバー代を払いたくなかったから。

その他

  • openapi-generator

Nature Remoのapiはopenapiの定義書が公開されています。
これをopenapi-generatorという、定義書からサーバーやapi clientを自動生成してくれるライブラリへ食わせてapi clinetは自動生成しました。
https://swagger.nature.global

vironは、node-vironlibというvironのサーバーを立ち上げるために必要になる色々なミドルウェアやcontrollerなどをnode.jsで実装してヘルパーライブラリとして提供していますが、d.tsを提供していないのと、今回そこまでしっかり実装するほどの内容でもなかったので、使わずに全て自前で実装しました。

機能

作った機能を紹介します。
まあ、とりあえずなんでもグラフ化すればカッコよく見えるだろうと思って、Nature Remoから取れる温度、湿度、照度を定期的にNature Remoのapiから取得してPostgreSQLに溜め込んで、その日1日分をグラフ化しました。
f:id:ishikawa_pro:20201024222148j:plain
上3つのカードコンポーネントは、現在の温度、湿度、照度を表示してます。

工夫した点は、10分おきにNature RemoのAPIを叩いてDBに保存しているのですが、HerokuのPostgreSQLの無料枠はrow数に制限があり、10分おきに行を追加していたらすぐに無料枠を失ってしまうので、1日のデータは1行にして、時間毎のセンサーデータはjson型にして1つのカラムに蓄積するようにしました笑
これでグラフ表示する時も今日の日付のデータを1つ引いて来ればいいので簡単です笑

あとは、とりあえず登録してあるNature Remoデバイスと、それらが管理している家電一覧などを表示できるようにしました。

f:id:ishikawa_pro:20201024224005j:plainf:id:ishikawa_pro:20201024223959j:plain
バイスなどの管理画面

このくらいの機能は、apiを叩いてちょっとレスポンスを整形して返してあげれば良いので実装は非常に簡単です。

まとめ

今回は、Nature Remoとvironを使って簡単なダッシュボードみたいなものを作ってみました。
Nature Remoのapiには、家電の操作系のエンドポイントも用意してありそうだったので、vironからエアコンの制御とかもできるようにすると、より管理画面らしくなって面白いかもしれないです。
TypeORMを使ってみた感想は、書き方がActive Record風な書き方や、SequelizeのようなData Mapperのような使い方が用意されていたり、スキーママイグレーションもできるようになっていたので結構リッチな感じでした。
今回作ったサーバーアプリケーションは、一応GitHubに上げておくので、cloneするなりforkするなりして、煮るなり焼くなりしてください。
github.com

jestのテストが遅い場合に確認すべきこと

こんにちは。
急に寒くなりましたね。
僕は最近iPad Air4 を予約しました。

今日は、javascriptのテストフレームワークのjestについてです。
業務では去年くらいから新規サーバーアプリケーションはjavascriptではなくTypeScriptを使っています。
それに合わせてテストフレームワークもmochaからjestへ移行しました。

jestjs.io

jestの特徴の1つは、一意なグローバル状態を持つことを保証しつつ、複数のテストを別プロセスで並列に実行してくれるため、安全に高速なテストをすることが可能なことです。
しかし、僕が前にいたチームでjestのテストにかかる時間がやたら長いことが話題になってました。
今日は、この高速なはずのjestがやたら遅い問題が社内で話題になっていたので調査してまとめてみたので、もしjestが遅くて困っている方は参考にしてみてください。

現状の問題

テストは基本的に、開発者のローカルPC(mac)でコンテナなどは使わずに走らせているのと、gitのサブブランチにpushした際などにgoogle cloud build を使ったコンテナ上のテストの2箇所で走っています。
そのローカルとCIどちらとも非常に遅い状態でした。
今回はこの2つの環境での問題について調査しました。

macでjestが遅い問題

まずは、mac上で遅い問題について調査しました。
実際、僕がいた時も若干遅さを感じていましたが、それからさらにコードとテストが増えていき、テストにかかる時間も比例して長くなっていったようです。
現状、51個のTest Suiteと285個のTestがあります。
タイムは、npx jest --clearCache でキャッシュを消した状態で実行すると約69秒でした。

Test Suites: 51 passed, 51 total
Tests:       285 passed, 285 total
Snapshots:   0 total
Time:        69.496s

同じコードでは無いので厳密な比較にはなりませんが、Node.jsとmochaで書かれた別のアプリケーションでは、テストの数が1000個を超えてますが30秒くらいしかテストの実行にかかりません。
それに比べるとテストの数が3分の1しかないのに速度が倍以上遅いです。

pprofでプロファイル

最近、node.jsでもpprofが使えるということを知って、プロファイルしてみるのがマイブームなのでとりあえずpprofを試してみました。
参考記事
moznion.hatenadiary.com

会社のコードのため、セキュリティとか色々な関係でプロファイルしたものは載せませんが、testごとの初期化がmaxで2~3秒かかっているものはあったものの、テスト1つ1つに大きな問題はなさそうでした。
メモリもそれぞれのテストでは特別多く使ってる感じはなかったです。

htopでCPUなどの使用率をみる

次にhtopコマンドで、テスト実行中のCPUやメモリーの使用率を確認しました。
ちなみに会社で支給されているマシンはMacBook Pro (15-inch, 2017) で、クアッドコアIntel Core i7です。(近いうちに新しいモデルにする予定)
こちらがテスト実行中のhtopの画面。
f:id:ishikawa_pro:20201018182058p:plain あんまりCPUとか詳しくないですが、hyper threading により物理コアは4つですが、PCからは論理プロセッサを含めて8つのコアがあるように見えているので、htopでの使用率も8個表示されています。
そして物理コア分しか使用率が振り切っていないことがわかります。

続いてこれは、jestで実行中のtestの数です。
f:id:ishikawa_pro:20201018182933p:plain
7つ同時にテストを走らせていることがわかります。
コードを確認したところ、ここで最大worker数を決めているようです。
https://github.com/facebook/jest/blob/a1adaff21db93517e60be840303518490cd5d51f/packages/jest-config/src/get_max_workers.js#L20

const cpus = os.cpus().length;  // 8
return Math.max(argv.watch ? Math.floor(cpus / 2) : cpus - 1, 1);

しかしhtopを見る限り論理プロセッサは全く使われていない様子なので、4プロセッサで7つworkerを管理している状態でした。
そこで、最大worker数を4つに制限して再度テストを実行してみました。
worker数は --maxWorkers オプションで指定できます。

Test Suites: 51 passed, 51 total
Tests:       285 passed, 285 total
Snapshots:   0 total
Time:        60.062s

結果10秒早くなりました。(キャッシュをクリアした状態)
キャッシュが効いた状態のテストだと30 ~ 40秒くらいになったので、まあ今までよりはストレスフリーになったでしょうという感じです。

考察

結局mochaを使ってるプロジェクトの方が速度が早いですが、mochaで書かれたテストはテストごとに状態が一意ではなく、globalに1度セットアップした状態を共有してるので、最初の初期化が終わってしまえばテストが直列実行でも高速です。(書き方にもよると思いますが)
しかしjestはそれぞれのテストが独立していて、一意なグローバル状態を持つことが保証されていることが売りなため、テスト毎に初期化があってそれに時間がかかってしまうことはしょうがない気がします。
なので、それぞれが分離されて独立していることによる、より正確なテストとのトレードオフなのかなと思いました。

CI上ではさらに遅い問題

一旦ローカルでの速度問題は解決したということで、次にCI上での問題についてです。
CIはgoogle cloud buildを使っていて、コンテナ上でjestが実行されています。
一番遅かった時期が、ちょっと前の話だったのでログがどこにあるか分からなかったのですが、7分くらいはかかっていたそうです。
とりあえずCIと同じよな環境で実行すべく、ローカルにコンテナを立ち上げてテストを実行してみました。
実行中のhtopの画面がこちら。
f:id:ishikawa_pro:20201018193536p:plain
コアが全部振り切ってて、良さそうに見えてましたが、いつまで経っても一向にテストが進まないので途中で諦めてしまいました。
コンテナの使えるCPU数を減らしたりなど色々試しましたが遅いままでした。
おそらくcloud build上でも同じことが起こってるんだろうと思います。
それで、ググってみたところ公式ドキュメントのトラブルシューティングに、DockerやCIサーバー上でjestが遅い場合の対処法が書いてありました。

jestjs.io

どうやら、高速SSDとマルチコアCPUを搭載した今時のPCなら高速だけど、特定のセットアップでは低速になる場合があるようです。
このドキュメントにリンクされているissueにも、circleCIやTravisCIでテストを実行するとlocal pcに比べて遅いことなどが報告されてました。

github.com

対応方法としては、 --runInBand オプションを付けて直列にテストを実行させると早くなるらしいです。
なんと最大50%早くなったとの報告も。

実際にローカルのコンテナでも直列実行したところ、並列だといつまで経っても1個もテストが進まなかったのに対して、直列だとちょっと遅いですが確実に進んでました。
時間も3分ちょっとくらいにはなりました。

Test Suites: 51 passed, 51 total
Tests:       285 passed, 285 total
Snapshots:   0 total
Time:        202.734s

考察

traviceCIなどのVMベースのCIだと、CPUのコア数をみて、適切な worker数を指定してあげれば高速になるそう。
それでも解決しない場合は、 --runInBnad で直列に実行させるしかなさそうです。
あとは、ちょっと微妙ですが、テストをディレクトリ毎に分けて実行するようにして、cloudbuild上でディレクトリごとのテストを別ステップとしてパラレルに行うとかすれば早くなるかもなと思いました。

まとめ

今回は、ローカルとCI上でテストが遅かったので、原因を探るために色々調査しました。
ローカルの方は、ちょっとパフォーマンス改善できたのでよかったです。
正直CI上で遅い時は、直列実行するしかないってのは、jestの旨味が半減してるようで微妙だなとおもいました。
もっといい方法がないか、今後も調査してみます!

『マイクロサービスパターン』を読んだ

こんにちは。
今日は、マイクロサービスパターンという本を読んだので感想的なメモです。
ちょっと前まで、技術書を電子書籍で読むチャレンジをしてましたが、どうも電子書籍だと続かないので、最近は諦めて物理本を買って読んでます。

買ってから知りましたが、この本の翻訳版らしいです。

Microservices Patterns: With examples in Java

Microservices Patterns: With examples in Java

読んだモチベーション

僕は1年くらい前から、10以上のマイクロサービスで運用されているサービスの開発に関わっています。
既存のマイクロサービスに機能を追加したり、新しいマイクロサービスを開発していて、あんまりマイクロサービスの実装パターンや設計方法について知らないなと最近思い、もっと色々なデザインパターンを学びたいなと思って、今回この本を読んでみました。

感想

自分の関心がある部分以外は結構端折って読んだので、かいつまんで感想書きます。

マイクロサービス間の通信方法について

3章でマイクロサービス間の通信方法について、同期的な通信と非同期メッセージングの2通りの方法について解説されてました。
普段の業務では、一部で非同期メッセージングを利用してますが、ほぼRESTを使った同期通信を使ってるので、非同期メッセージングの使いどころなどは勉強になりました。

マイクロサービスアーキテクチャでのトランザクション管理

4章でマイクロサービスアーキテクチャでのトランザクション管理について、サーガという非同期メッセージングとローカルトランザクションを使ってデータ整合性を維持するパターンを踏まえて解説されてました。
サーガ自体を知らなかったので勉強になりました。ただ、イベント駆動で非同期的にやり取りするパターンだけで全てのユースケースは網羅できないと思うので、同期的な通信でのトランザクション管理のパターンも知りたかったです。
この本全体で、マイクロサービスでトランザクション管理といえばサーガという感じでサーガ全推しな感じでした。
他のパターンも気になって調べてみましたが、tcc(try/confirm/cancel) というパターンもありました。 engineering.mercari.com 内容を読んでみると、業務で使ってるやりかたがtcc(choreographyっぽい)だと気づきました笑

クエリー実装について

7章でマイクロサービスアーキテクチャでのクエリー実装について解説されてました。
単純に複数サービスからデータを取得して合体させるAPI compositionパターンと、それでは表現できない複雑なクエリーの場合は、専用のテーブルと場合によってはそれ用のサービスを用意して効率よくクエリーを実装しましょうというCQRSパターンについて解説されてました。
普段は、ほぼAPI compositionで頑張ってたので、CQRSについては、勉強になりました。

まとめ

マイクロサービスアーキテクチャでの色々な実装パターンや設計などを知りたかったので、この本はとても丁度良い本でした。
普段の業務で使ってる技術スタックに置き換えて、さらに深堀って勉強していきたいです。
他にもモノリスからマイクロサービスへの移行をするための設計方法やインフラ回りなどについても解説してあるので、そっちの方に興味がある方にもおすすめだと思います。

おわりに

次に読む本は、データ指向アプリケーションデザインにしました。

分厚くて読むの大変なので、あえてここで読むことを宣言します笑
結構苦手分野っぽいですが、知りたいことがいっぱい書いてありそうだったので頑張って読みます。

ISUCON10 予選に参加しました

ISUCON10予選に参加したので、今日は参加報告と振り返りです。

isucon.net

昨年が初参加だったので今回が2回目の参加です。
ギリギリまで参加登録してなくて、twitterで残り100チーム切ったとかの報告があって、慌てて参加登録して滑り込みセーフな感じでした。(480番だった)
会社の同期5~6人と今年も出たいねと言っていましたが、僕以外だれも参加登録してなかったため、誰と出るかで揉めるのが嫌だったので、今回はこっそり1人で参加しました。

去年の自分のブログ見ながらやること思い出して作業してたので、今年もやったこと書き留めておきます。

使った言語と結果

昨年は、Goやってみたかったので事前にGoの勉強をしてGoで参加しました。
今年は、慣れた言語がいいなと思って、普段業務で使っているNode.jsで参加しました。
大体過去問みるとkoa.jsが多かった気がしてたので、今年もkoa.jsだろうなと思っていたら、いつも業務でお世話になってるexpress.jsだったので、心理的障壁がかなり少なくてよかったです。
余裕なくて動かしてないですが、今年はDenoの参考実装もあってびっくりしました。

次に結果ですが、471点スタートで、最高スコアは1006点、最終スコアは 998点でした。
ちょっと長いですが、下記がスコアの流れです。
19:00頃まで500点台をウロチョロしてましたが、そこから900点台まで一気に上がりました。
f:id:ishikawa_pro:20200915214328p:plain

どうでもいいですが、ベンチマーカーを一番最初に回しました笑

やったこと

12:20 ~ 13:00頃

sshでマシンに接続できてからは、node.jsのコードをgithubのprivate repositoryへプッシュしました。
github.com

基本的な作業フローは、手元のPCにcloneしてVSCodeで編集して、ブログにまとめやすくしようと思って、一応プルリク作ってmasterにマージしてました。
masterにマージしたものは、sshインスタンスに入って手動でpullしてアプリケーションを再起動してました。

13:00 ~ 14:00過ぎ

次にレギューレーション全部読んで、 botからのリクエストは 503 Service Unavailable を返しても良いと書いてあったので、それを実装することにしました。 ISUCON10 予選マニュアル · GitHub

最初Nginxでやろうと思いましたが、Nginxのconfを書くのになれてなかったので、expressのmieddlewareで書いても対して変わらんだろうと思い、Node.jsで実装しました。
このPRのあとから、色々手直ししましたが大体こんな実装です。
uaをみてbotなら503を返すmiddlewareを追加 by ishikawa-pro · Pull Request #1 · ishikawa-pro/isucon10-qualifying · GitHub

なぜか、459点とスコアがちょっと落ちました。
雑にcurlでしか動作確認してなかったので、コードが間違ってたか、リクエスト数が増えてDBのボトルネックがより目立つようになったかと思いました。
今考えたらちゃんと計測すればよかったですが、後者だと信じて引き戻さずに次の作業へ進みましたw

14:00 ~ 16:00 過ぎ

コード読んだり、NginxやMySQLの設定をみたりしつつ、Nginxのlog formatを変えてalpで解析できるようにしたり、MySQLでスロークエリーログを出すようにしてました。

16:00 ~ 17:00 頃

この変は記憶が曖昧ですが、スロークエリーログを見ながらMySQLにインデックス貼ったりしてました。
確か estate のrentにindex貼った気がします。
ALTER TABLE isuumo.estate ADD INDEX rent_index(rent);

ここでようやく初期スコアをちょっと超えて、518点とかになりました。

17:00 ~ 17:30

alpとMySQLのログから /api/estate/low_priced がわりとよく叩かれてるのと、 estateのlow_pricedが変わるタミングは /initialize 時と、csv入稿の時だけっぽかったので、オンメモリ上で管理するように修正しました。

api/estate/low_pricedの情報をオンメモリにキャッシュ by ishikawa-pro · Pull Request #2 · ishikawa-pro/isucon10-qualifying · GitHub

533点と思ったほどには上がりませんでした笑

17:30 ~ 18:00 過ぎ

またalp見直したり、slow query logみたりしてて、nazotteがやっぱり遅いよな〜と思ってコード見てました。
とりあえず下記SQLを、取得したesteteの数だけfor文で実行していたため、非同期処理をシリアルに実行するようになっていたのが気になったので、Promise.allでパラレルに取得できるように修正しました。

"SELECT * FROM estate WHERE id = ? AND ST_Contains(ST_PolygonFromText(%s), ST_GeomFromText(%s))";

nazotteで非同期処理をfor分で回していたのでPromise.allに修正 by ishikawa-pro · Pull Request #3 · ishikawa-pro/isucon10-qualifying · GitHub

これまたなんと、517点とスコアが落ちました。
コードは間違ってなかったのできっと、複数のSQL実行をシリアルからパラレルに修正したことでDBの負荷が上がったんだと信じて、これも引き戻さずに次へ進みましたw

18:00 ~ 19:00

結構お腹が空いてきてました笑
確かこの時間くらいに、nazotte用にestateにインデックスを貼りました。
ALTER TABLE isuumo.estate ADD INDEX lat_long_index(latitude, longitude);
531点とか落ちたスコア分が元に戻るくらいには上がった気がします。

そのあとは、コードを読んでも、極端に複雑なSQLが沢山あるわけでもないし、椅子の購入や資料請求もシンプルだったのでまだボトルネックではなさそうだなと思ってました。

19:00 ~ 20:00

マシンスペックが1コア2GBで、ベンチを回しなが htop を見てるとメモリはちょっと余裕あるけど、CPUは100%で張り付いてるな〜という感じでした。
まだMySQL自体のチューニングがほぼ手付かずの状態だったので、そっちの方をやるかという感じになって色々ググってました。
最終的にthred_cache_size, query_cache_size, innodb_buffer_pool_sizeなどをいじりました。
query_cache_sizeとquery_cache_limitはどれくらいが適切がよく分からなくて感覚で設定しました。
この辺は来年までに詳しくなりたい。

thread_cache_size = 151
query_cache_limit = 50M
query_cache_size = 512M
query_cache_type = 1
innodb_buffer_pool_size=1G
innodb_log_file_size=250M

この設定がすごく効いて、一気に900点台までアップして、色々微調整して1006点まで行きました。
このスコア見たときは声出ましたw f:id:ishikawa_pro:20200915235254p:plain

19:00過ぎで、たしか順位が真ん中くらいだったのが一気に61位まで上がったので結構テンションあがりました笑

結局パラメータいじり過ぎて、一番よかったパラメータが分からなくなり977点で着地。
ちゃんといいスコア出たときは、記録せねばと猛反省しました。

20:00 ~ 21:00

残り時間で何ができそうか考えて、インスタンスを1台しか使ってなかったので、1台目をMySQL専用機にして、とりあえず2台目をアプリケーション用サーバーにしてみようと思いました。 しかし、MySQLに繋ぐためにポートを開けないといけないが、この時間に焦って慣れないことしてインスタンスをダメにしたらヤバイなと思って諦めました。
来年までにLinux力も絶対上げたい。

最後の最後に /api/recommended_estate/:id でindex貼れそうだと気付いて、

ALTER TABLE isuumo.estate ADD INDEX door_index(door_height, door_width);

を貼ったら998点になり、slow query logの設定とかを無効にして終了。

後日公開された参考値も同じ998だったので再起動試験も問題なしでした。

感想

昨年は3人で出て、簡単な N+1を直して微妙にスコア上がって終了だったのが、今年は1人参加で初期スコアから倍以上アップして、後半に一時61位まで上がったので、1年で成長をすごく実感できてよかったです。
結局ラストスパートでめちゃくちゃ抜かれていったみたいで悔しかったので、来年こそは決勝いけるように頑張ります。

最後に、運営の皆様、お疲れ様でした。
決勝出場するチームの皆さんは頑張ってください!

chromeのreferrer-policyについて

[9/8追記]
chrome 85がリリースされたのに、defaultのreferrer-policy変わってないなと思ったら段階的に変わっていくみたいです。
A new default Referrer-Policy for Chrome: strict-origin-when-cross-origin


こんにちは。
業務でchromeのreferrer-policyについてちょっと調べることがあったので、今日は軽くまとめです。

referrer-policyとは

そもそも僕が、referrer-policyについてそんなに詳しくなかったのでまずは簡単な説明。
Referrer-policyは、リクエスト時にreferer headerにどれくらいreferer情報をつけるかを設定するためのヘッダーです。
refererの情報量とは、具体的に

  • origin, path, query 全て
  • originだけ
  • 情報なし

の3パターンがあります。

referrer-policy headerに付けられる値は、

  • no-referrer
  • no-referrer-when-downgrade
  • origin
  • origin-when-cross-origin
  • same-origin
  • strict-origin
  • strict-origin-when-cross-origin
  • unsafe-url

があります。
それぞれの値がどういう振る舞いをするかなどの詳細はMDNがよくまとまっています。

developer.mozilla.org

referer の正しいスペルは referrer ですが、RFCの標準化時にスペルミスしたものがそのまま登録されたという話は、 Real World HTTP で読んだ気がします。
referrer-policyの方は、後からできたので正しいスペルになってるそうです。

chromeのdefault値について

現在のchromeは、 デフォルトのreferrer-policyが no-referrer-when-downgrade になっています。
no-referrer-when-downgrade は、https => https や http => https などのセキュリティ水準が同一または改善される場合は、クエリなどの全ての情報が送信されますが、https => http のように水準が低下する場合は、refererを送信しない挙動になっています。
そしてここからが本題です。
chromeのdefaultのreferrer-policy が version 85から strict-origin-when-cross-origin へ変わるそうです。
www.chromestatus.com

strict-origin-when-cross-origin は、同一オリジンの場合はクエリを含む全ての情報を送信し、クロスオリジンの場合はoriginのみを送信、 https => httpへのセキュリティレベルの落ちる通信では情報を送信しません。
僕は、会社のPCのデフォルトブラウザをMicrosoft Edgeカナリアリリース版を使ってるのですが、EdgeはCanary v79と Dev v79から反映されていました。
stable版に、いつ反映されるかは確認できませんでした。(version 83.0.478.64現在は no-referrer-when-downgrade でした) docs.microsoft.com

今回、業務で開発してるアプリケーションでreferer headerを使っている部分があり、カナリアリリース版のEdgeでアクセスした場合に、挙動がおかしかったので気付きました。
ちなみに、僕の試した環境だとサブドメインが同じ場合でもクロスオリジンとみなされていました。 (originしか入っていなかった)

おわりに

今回は、referrer-policyについてと、chromeのreferrer-policyのdefaultが変更されることについてまとめました。
普段、カナリアリリース版のEdgeを使っていますが、今回初めて業務の役にたったきがします笑
ベータ版とかを積極的に使っていくこともやっぱり大事ですね笑

御拝読ありがとうございました。