ISUCON12 予選参加記録 (温かみのあるシェルスクリプト愛好会)
ISUCON12に予選に参加してきました。
チーム名 : 356 温かみのあるシェルスクリプト愛好会
メンバー
- よしだ (https://github.com/HirokiYoshida837)
- クエリ解析したりAPMみながらとりあえず色々やる担当
- すー (https://github.com/suecideTech)
- インフラ・ミドルウェア類を主に触る担当。
※ざっと大きな役割担当は考えつつも、ふたりともできることを色々やる方針。
以下タイムライン
■前日まで ISUCON開始前練習
3週間くらい前から過去問を練習したり、色々情報しらべたり、秘伝のタレをつくったり、TODOリストを作ってました。
GCP上での環境構築が簡単にできたので、個人練習 + なんどかリハーサルを実施。
-
事前練習しながらMakefile類や各種手順、観点とかを整理
-
インフラ秘伝のタレ類も整備
-
当日のタイムスケジュールを整備
-
自分で練習をしてる時点では、「npm scripts + zx で、makefileの代わりみたいなことやるか~~」と思ってやっていたが、練習中にnodeのバージョン問題とかに何度かあたったのでMakefileへ移行。
- ISUCON12ではローカルにnode.jsもnpmもなかったので結果的に正解だったかも。
-
DatadogのAPMをつかってISUCONやることを検討して、入れる練習を何度かした。
- ISUCON中でのクエリ解析や各種エンドポイントの負荷分析は alp + pt-query-digestが定番
- ログローテーションの管理とかが苦手、失敗しそう、という思いもありなにかしらのAPMをアプリに埋め込む方向に。
- 普段仕事で触らないし、せっかくなので個人的にDatadogを使ってみたかったという思いで採用。
- ホスト側はhtop+dstatである程度把握できそうだったのでAPMは使うが、ホストやMySQL、Nginxの解析はDatadogではやらない方針に。設定も大変だし。
-
過去問はISUCON10, 11を主に練習。
■本番 9:30~ 開始前準備
- 無事起床してDiscordで各種チャンネル類作成、GitHubの準備。
- 事前配信みながら今年はなにかね~と雑談。
■9:55 予選問題 ムービを閲覧
- isuports
- ダッシュボードとかのグラフ表示の負荷絶対やばいよね、と予想
- RedisのSortedSet使いたいね
- グラフ表示やデータ表示部分にN+1問題あるわ~~~とか言ってた。
■10:00~ 予選開始 (Score : 2841)
-
CloudFormationでスタック構築。5分くらいで終わって感動
-
早速sshしてサーバーの中身確認。
- メモリ4GBあるので、vscode remoteで色々やれそう。
- アプリがDockerでうごいてることに気づく。
- ざっとファイル確認、DBどうなってるのこれ?
-
自分がgit用の各種設定を実施
- git initしてGitHubに初期コミットを実施
- 初期データsqlや xxx.dbファイルが重たすぎてgit側に入らないので確認
- このときにsqliteあることに気づく。マジ?と叫ぶ。
- 2,3 のサーバーもgit initして、GitHubのリモートリポジトリを登録
- 実はこのときに色々ミスしてた。11時くらいにそれに気づく
- git initしてGitHubに初期コミットを実施
-
git類整備してるあいだに、suecideTechにコードをみてもらって怪しそうな箇所にコメントを入れてもらう
- ついでにhtopみながらベンチ回してみて、色々やばそうな実装あることを確認
■11:00~ CloudFormationスタック再作成
-
初期のgit init時に、2,3のサーバーの処理をミスってデータを壊してしまった
- 落ち着けば復旧できたかもしれないが、そんなに作業もしてない + サーバー1でgit initしてGitHub側にPushできてるので、スタック再構築する方向で相談
- まじで申し訳なかったが、suecideTechが「ええんやで~~。オンスケだしゆっくりやってこ~~」とフォローしてくれて落ち着いた。感謝。
- 壊した原因は、練習時のisucon11用リポジトリをremoteに設定してgit pullしたりしてたこと。練習時に作成した手順書の直し漏れだった。
-
無事にスタック再構築。
- こんどは問題なくGitHubをとおしてそれぞれのリポジトリを同期。
■11:30 nginx, mysqlに秘伝のタレ導入
suecideTechがシュッっと設定実施。 スコア忘れた
■11:40~ DatadogのAPM設定実施 (Score : 2619)
Datadogのトレーサー設定してAPM開始。sqliteの解析はぱっとググれなかったのでとりあえず放置して導入。
- Dockerでアプリが動いていたので、ローカルのDatadog-Agentにどうやってデータを渡すか悩み。network_mode: host でDocker Composeに記載があったのに気づけたので特に設定は不要だった。
ベンチをまわして、各種エンドポイントの負荷状況を確認。
- ランキング処理が重たいよね、を把握。(実際、画面みても重たかったのでそれはそう)
- クエリ結果みながら、「これどうやって高速化する?」と相談。
SELECT player_id, MIN ( created_at )
FROM visit_history
WHERE tenant_id = ? AND competition_id = ?
GROUP BY player_id
REPLACE INTO id_generator ( stub )
VALUES ( ? )
※予選がおわったあとに画像みてるので粒度が荒いです
■11:40~ APとDBの分離を実施 (Score : 3507)
suecideTechがシュッっと設定実施。Server1をAP、Server3をDBに。
■12:00 休憩しながら色々眺める
-
Datadogやhtopみながらデータを眺める
-
実装をみてsqliteどうしようか、と相談
- sqliteをMySQLに移行することを検討。
- sqliteだと扱いづらい、クエリのExplain確認などが大変そうだね、という理由から移行を検討
- 移行をsuecideTechにほぼお願いした。
-
これ初期化どうなってるの?を確認
- /initialize でDBが作り直されてないことを確認。
- 最近の練習でやってなかったパターンなので焦る。
■14:00 (Score : 3921)
MySQL側にいくつかIndexをはったり(sqliteのIndex貼り方がわからなかったため)、select * を地道になおしたりしてスコアアップ。
- 実際、効いてたのかはよくわからず。誤差かもしれない。
■14:30 sqliteの移行がなかなかうまくいかない
移行したときのDB構築がうまくいかなくて苦戦。
-
二人で、初期データの入れ方や初期化方法を確認しながら、どう入れればいいのかを検討。
-
DBみながらデータ入ってる?いやきてない?を繰り返す
-
journalctlのログから接続エラーの原因をさがしながら作業
- 今思えば、怪しそうな箇所のErrorログを適切にだして、ログ出力レベルを変えればもっとやりやすかったと思う。
-
作業途中で、Server3 が何度も固まって焦る。
- ちゃんと/var/log確認できてないが、DBの書き込みとかでOOMになってたと思う。
- うんともすんとも言わないので、AWSコンソールから強制停止して再起動で復旧させる (合計で4回くらいやった)
- MySQLの設定をすこし触ってもらって、メモリ使用量とかをチューニング。
■15:00 sqliteをMySQLに移行完了 (Score : 0)
Server3にsqliteのデータを移行完了。 ベンチ実施 -> verificationで止まってスコア0。
- 負荷状況をみると、DB用のサーバー3がCPU 100%張り付きでレスポンスをAPに返してないことが判明
- DBが一台だと負荷が大きすぎると考えDBを2台構成にする方針に決定
- このとき、原因をちゃんとみれてなかったのに、この判断をしてしまったのは良くなかったかも。
- IOが悪いのか、なにが悪いのかを見れば、対処できてたかもしれない
■15:30 Server2をMySQL用インスタンスに、旧sqlite分は Server3に移行 (Score : 0)
DBのテーブルを分割したが、効果なし。
- 二人で焦る。
- 原因をちゃんと追求できず。
- Server1をAPサーバー+sqlite、 Server3を mysqlに切り戻すことを決定
- GitHub上で色々Revert実施。
■16:20 Score更新処理をBulkInsertに変更(Score : 4493)
Server1をAPサーバー+sqlite、 Server3を mysqlに切り戻し完了。 BulkInsert化できる箇所をさがして実施。
すこしスコアが上がったかも。
■16:00 sqliteのIndexを追加していく (Score : 6191)
自分がBulkInsert化できる箇所をいくつか修正してるのと並行して、Index貼ったりしてもらった。
sqliteのIndexを調べながら貼っていった。
- 貼り方がわからず。DBのファイル全部に対してスクリプト実施しないといけない?
- シェルスクリプトでfor文をぱっと書けなかったので、手でひたすらindex追加実施。
- 温かみのあるシェルスクリプト同好会とはなんだったのか
- このときには、手ではったindexは初期化時にきえないとわかっていたので安心して作業。
- 途中で自分が 一括でバッとSQL実施するようのSQLを作成したりする。
どっちの効果があったかわからないが色々あってスコアがはねて二人してテンションがあがる
■16:30 N+1の解消できそうな箇所を探してトライ (Score : 0)
リーダーボードが遅いよ、とベンチに言われているので直そうとトライ。 N+1をいくつか解消できればスコアが大きく上がりそうな気がしたので挑戦した色々あせって実装ミスして通らない。
- 時間がなさそうになってきたので途中で諦め
ダッシュボード閲覧時に閲覧履歴のInsertがあったのを、goroutine化するのもアリかと思ったがこれは手がでず、、、。
- 練習不足だった
あきらめて切り戻しを実施
■17:00 ベンチガチャ・再起動試験・後片付け (Score : 6236)
Datadog-Agent停止、各種ログ停止しながらベンチガチャ実施。あとは祈るしか無い。 再起動試験も、いくつかパターンを試して実施して、問題なさそうなことを確認。
Datadog-agent停止前に取った最後の記録がこんなかんじ
■18:00 終了
対戦ありがとうございました。
■ 最終スコア
7/24発表のスコアは 5838 : 167位 (参考) でした。
■ 参考 : ベンチスコア履歴
■感想・振り返り
3回目の参戦で、今までよりも確実にできることは増えて作業はできたはず(それでも遅い箇所や、作業ミスは多かったので反省) ユーティリティ用のMakefileの準備、各初期化処理の手順、datadogのAPMも導入して、丁寧に計測・確認・修正・実行のイテレーションは回せました。ここは練習や事前準備の成果を出せたとは思います。
Datadogやhtopを見ながらボトルネックの箇所をみつけて対応をしようとはできましたが、実装や対応が今ひとつ力およばずです。
sqliteや、直近2年とは大きく違う実装であったことなどに戸惑って、大きなボトルネックを潰すことができなかったが悔しいですね。 MySQLへの移行は、もうすこし落ち着いてdstatなどをみながら考えていれば原因を解消できていたかもしれません。
ただ、ヤバいと思い元の構成に二人ですぐに切り戻して、できる改善をやっていく方針に頭を切り替えられたのはよかった点かも。 もっとスプリント的に時間を区切って、適宜判断ポイントを設けるのも一つの手だったかな。
Redisも使えるよね、とは言ってたけど結局導入できず。ローカルでRedisがうごいていることに気づいたのは17:00頃でした。大ヒントじゃん。
他、個人的にDatadogも使いたいと思っていたのを使えたので満足です。APMでの分析・クエリ分析が導入さえできれば手をわずらわせることが無いのでとても快適。費用は高いので来年はどうするか考え中。セルフホストのElastic APMにするか?
また、去年までは雰囲気でGoの実装を読んでいましたが、Goのコードを読み書きするようになったので去年よりはアプリ側の改善にも取り組めました。(の、割には大したことはできなかったですが)
あと悔しいのは、sqlite3でのIndexを大量に貼る作業などのシェルスクリプトを、サッと作れなかったことが悔しいですね。sed・awkを始めとするshell芸力があればよかったのだろうか。このあたりが楽になるようにzxを導入する作戦だったんですけどね。
今回は初メンバーと一緒に参加した割には、事前練習も本番の連携もうまくできてそれなりに連携できたと思います。 いろいろ気を使ってもらったり、落ち着こうぜ~と、ペースを作ってくれたsuecideTechに感謝です。
色々心残りな点は多いので、予選問題のリポジトリができたらすぐに復習したいです。
来年もぜひ参加したいですね、sqlite3最高!
ではでは!