ISUCON12予選に参加した

ISUCON12予選に参加しました。 最終ベンチ結果は6815点で、最高ベンチ結果は10625点、failの可能性は高いです。 もし結果発表でfailしていたらブログを書く気に多分ならないと思う(あと記憶力が弱い)ので、今のうちに当日やったことを中心に振り返っていきます。

追記 (2022/7/25): failしていました :cry:

自分がやったこと

ファイルロックがボトルネックになっているっぽいことを見つけた

alpのログを見ると、ランキングやプレイヤー詳細のAPIボトルネックになっていることが分かりました。

alpだとエンドポイント単位でしか見られないんですが、pprofはポートを立ててWeb UIから見なければ詳しくは見られない上にちょっと見づらい・・・ということで、このときから作っていたツール1 を使って、関数ごとにかかった時間を計測しました。

alpで一番重かったランキングAPI (competitionRankingHandler) を分割して見たところ、一番時間がかかっているのはSQL文ではなく、ファイルロックをしている部分(competitionRankingHandler 4)でした。

ファイルロックをする関数(flockByTenantID)も、時間がかかっていることが分かります。

ファイルロックを消した (failの戦犯)

お題のアプリケーションはマルチテナントSaaSで、管理側はMySQLを使っており、テナントに入っている側はそれぞれ別のSQLiteを使っていました。

ロックをかけている部分のコードには、

// player_scoreを読んでいるときに更新が走ると不整合が起こるのでロックを取得する

とコメントがあるのですが、DBの読み込み時にかかっている排他ロックが、読み込みが非常に遅い原因になっていました。

読み込み時には共有ロックをかけると読み込みが同時に行えて性能が上がるだろうということで、ファイルロックをやめてSQLiteのトランザクションを使おうとしました。

トランザクションに書き換えると、error retrievePlayer: error Select player: id=395f19764, database is locked というエラーが出てしまうものの、スコアは5000点から10000点近くまで上昇しました。

追記 (2022/7/25): 正しくトランザクションが貼れていてもこのエラーは出るので無視してよかったらしい。そんな・・・

いろいろ試行錯誤した結果、このエラーが出たり出なかったりしてスコアが0になったり10000になったりしました。これが失格の原因になるのは困るということで、⚠️ロックを一切かけない⚠️ことにしました。このあたりで既に完全に沼に入っているので、冷静な判断ができていればrevertして5000点から再開していたと思います。失格になったらこの変更のせいなのでごめんなさい(泣)

リーダーボードで上位だった方のコミット履歴を見ると、SQLiteからMySQLにテーブル単位で移していました。 SQLiteのロックを外すことを考えるよりも先にその戦略を取れば、普段使っているMySQLトランザクションが使えるのでやりやすかったと思います。 SQLiteからMySQLへのリプレースは弊チームではリスクが高いと判断して取り組まなかったことを考えると、部分的に置き換えるというのはやはりISUCONにおいて重要戦略であると思いました。

追記 (2022/7/24): ファイルロックをsync.RWLockとsync.RLockにする案は思いついたんですが、tenant IDごとにロックを分けるためにmapを作る必要があって、そのmapを管理するためにロックが必要で・・・となって面倒くさそうでやめてしまいました。tenant IDは固定でmapはreadだけなので、本当はそんなに難しくなかっただろうなと思います・・・こっちでやればよかった:cry:

ファイルロックをRLockにもしてみたんですが、failしたのでロックが取れるまで待つ実装は自前でやらなければいけないのかなと勘違いしました。It will wait until it is able to obtain the shared file lock. とあるので、その方針でも自信をもってやればできただろうと思います。

https://pkg.go.dev/github.com/gofrs/flock#Flock.RLock

チームメンバーがやったこと

IDジェネレータをUUIDにした (AokabiC)

スロークエリログを見ると、id_generatorという謎テーブルへのINSERTが一番遅いことが分かりました。被らない連番IDを取るために、MySQLにわざわざレコードを追加して採番していたので、これをUUIDに変更してくれました。

  REPLACE INTO id_generator (stub) VALUES ('S')

AokabiC氏の目の付け所はいつも洗練されているのですごいなあと思います。

App, DBの2台構成にした (zatton)

ISUCON12予選では3台のサーバーが与えられますが、AppとDBを分離するのは、 - htopをするとAppとDBのどちらがボトルネックになっているかわかる - 負荷が軽くなる の2点のメリットがあるため、早い段階で分離しました。

弊チームは、/etcなどの設定ファイルは権限周りが面倒なのでGit管理せずにzatton氏がVimでゴリゴリ直接書いていく方針で、zatton氏がサクッとやってくれました。

visit_historyへの書き込みを減らす (AokabiC)

visit_historyはランキング取得のタイミングで履歴を書き込むのですが、このテーブルは、最新のものしか取得されていません。そこで、visit_history_summerizeというテーブルを作って、最新のものだけを書き込むようにしました。インデックスも貼りました。

自分がやろうとしたこと

player_score を剥がす

ファイルロックがボトルネックから外れた後は、player_scoreテーブルを使う関数がボトルネックになってきたのでこれをRedisに移そうとしました。しかし、整合性チェックでfailしてデバッグする時間もなく断念。RedisではなくMySQLにInsertしている方が整合性に関して安心できるという点で良い方針だったのかもしれません。

チームメンバーがやろうとしたこと

3台構成にする (zatton)

3台構成にする際は、Appを2台(nginx+AppとApp)にするかDBを2台(リードレプリカや垂直分割)にするか、その他の構成にするかが悩みどころです。 今回は、2台構成にしたところAppのCPU負荷が100%に張り付いていたので、Appを2台にすることを考えました。しかし、Appサーバーのファイルシステム上にはSQLiteがあり、Appを2台にするならNFSなどで同期しなければいけないことが分かります。ということで、これは一旦放置することにしました。

player_scoreへの書き込みを減らす (AokabiC)

player_scoreもvisit_historyと同様に書き込みが減らせそうということで、取り組んでいましたが時間が足りず断念。

感想

既存のISUCON攻略法を封じてくるような問題だったので、面白かったです。 具体的には、DBがSQLiteMySQLに分離されていたので、MySQLのスロークエリログには出ないボトルネックがありました。

余談

当日は、SQLiteMySQLがトレンド入りして盛り上がりました。

ISUCON界隈の外からはMySQLトレンド入りの原因が謎だったっぽい。

ここは違うよ、というのがあれば、TwitterのDMで教えてもらえると助かります。


  1. 本当はこういう感じのラインプロファイラがGoにも欲しいんですけどね・・・ 自作ツールは需要があったら公開したいですが、品質が低すぎるので準備中です。

セマンティックセグメンテーションでやっていく年賀状作成 2022

この記事は、CAMPHOR- Advent Calendar 2021 の3日目の記事です。

概要

年賀状やメッセージカード作成に役立つWebツール、SSフォトカードメーカー を作りました。 このツールを使うと、写真からこういった画像を作成できます[^1]。 f:id:shiba6v:20211202205444j:plain

全面写真の年賀状と「上に文字が乗らない問題」

年末といえば、年賀状作成ですね。最近はSNSで送る人もいるみたいです。 テンプレートに小さい写真を入れていくのではなく、全面が写真の年賀状もおしゃれですよね。

続きを読む

論文のPDFから図を自動で抽出する

この記事は、CAMPHOR- Advent Calendar 2020 の2日目の記事です。

論文のPDFから図を自動で抽出する

こんにちは、シバニャンです。最近やっていることと言えば、修士論文の執筆です。修士論文を書くためには、普段から大量の論文を読んでおく必要があります。僕の研究分野はコンピュータビジョンという画像を中心に扱う分野で、年数回、国際会議があるたびに1000本程の論文が発表されます。その中から自分の研究に関係があったり、直接関係はなくても役立ちそうなアイディアがある論文を見つける必要があります。

論文ギャラリーを作りたい

f:id:shiba6v:20201201102848p:plain

続きを読む

LaTeXで下線の引き方

近況報告

こんにちは. 最近は,DC1の申請書を書いていました. 業績が全然無いので,運良く通ったらいいな〜くらいの気持ちで書き始めたんですが, 先生や先輩から文章の書き方について学ぶ良い機会になりました.

(今まで自分が書いてきた文章は,日本語ではなくて別の何かだったという気持ち・・・)

科研費LaTeX最高!

DC1はWordを使って書くことが推奨されていますが,Wordはやっぱり使いたくないので,代替として用意されている科研費LaTeXを使いました. http://osksn2.hep.sci.osaka-u.ac.jp/~taku/kakenhiLaTeX/

続きを読む

配列の形状(Shape)のコメントを付けるJupyter Notebook拡張を作った

加筆(2019/12/12)

注意 JupyterNotebook拡張は使いづらかったので,IPython拡張としてリニューアルしました. こちらを使ってください.

github.com

このページを読む代わりに,下の記事を読むことをお勧めします. (検索順位はこのページの方が高いので微妙な気持ちです・・・) shiba6v.hatenablog.com

続きを読む

日本語のJupyter NotebookをPDFとしてダウンロードする裏技

Jupyter NotebookをPDF化する際にLaTeXを入れて頑張る方法がありますが,もっと楽をする方法があります.

ブラウザの機能を使って印刷すれば良いんです.

f:id:shiba6v:20190627235218p:plain
notebook

続きを読む

toxからbatsを使ってPythonのバージョンを変えながらシェルスクリプトでテストする際のエラー

こんにちは. この前リリースしたPythonで使えるツールのshape_commentatorの評判が良く,最近はCIでPyPIにパッケージをアップロドする設定を書いたりしてCIとわいわい遊んでいます.

続きを読む