Closed15

ISUCON12予選参加記録 (メモ)

vintersnowvintersnow

ISUCON 12予選の参加記録です。
ISUCON自体ほとんど知らず、普段はiOSメインのエンジニアをしています。
ISUCON本を片手に知り合い二人と参加しました。

文章は競技中の取ったログの切り貼りなので、読みにくいかもしれませんがご了承ください。

vintersnowvintersnow

APIのエンドピントごとに処理が出来たら点数が付き、エラーを返したら原点されるという方式。
最初はgo実装だったので、そのまま一度ベンチを動かした。

初期スコア 2200点

vintersnowvintersnow

ボトルネックの手掛かりを手に入れるために、下記のログを記録するように変更

  • Nginx
  • MySQLのslow query
  • pprof
vintersnowvintersnow

1. Nginx + alp

alpのインストール

wget https://github.com/tkuchiki/alp/releases/download/v1.0.11/alp_linux_amd64.zip
unzip alp_linux_amd64.zip
sudo install ./alp /usr/local/bin

nginx.confに下記を追加

log_format json escape=json '{"time":"$time_local",'
                            '"host":"$remote_addr",'
                            '"forwardedfor":"$http_x_forwarded_for",'
                            '"req":"$request",'
                            '"status":"$status",'
                            '"method":"$request_method",'
                            '"uri":"$request_uri",'
                            '"body_bytes":$body_bytes_sent,'
                            '"referer":"$http_referer",'
                            '"ua":"$http_user_agent",'
                            '"request_time":$request_time,'
                            '"cache":"$upstream_http_x_cache",'
                            '"runtime":"$upstream_http_x_runtime",'
                            '"response_time":"$upstream_response_time",'
                            '"vhost":"$host"}';

access_log /var/log/nginx/access.log json;

nginxを再起動

sudo rm /var/log/nginx/access.log && sudo systemctl restart nginx

ALPでサマリを出力

sudo cat /var/log/nginx/access.log | alp json -m "/api/player/competition/.+/ranking","/api/player/player/.+","/api/organizer/competition/.+/score","/api/organizer/competition/.+/finish","/api/organizer/player/.+/disqualified" --sort=sum -r

+-------+-----+-----+-----+-----+-----+--------+---------------------------------------+-------+--------+----------+-------+--------+--------+--------+--------+------------+------------+-------------+------------+
| COUNT | 1XX | 2XX | 3XX | 4XX | 5XX | METHOD |                  URI                  |  MIN  |  MAX   |   SUM    |  AVG  |  P90   |  P95   |  P99   | STDDEV | MIN(BODY)  | MAX(BODY)  |  SUM(BODY)  | AVG(BODY)  |
+-------+-----+-----+-----+-----+-----+--------+---------------------------------------+-------+--------+----------+-------+--------+--------+--------+--------+------------+------------+-------------+------------+
| 708   | 0   | 668 | 0   | 40  | 0   | GET    | /api/player/competition/.+/ranking    | 0.012 | 14.520 | 1150.077 | 1.624 | 7.356  | 8.720  | 14.320 | 3.248  | 0.000      | 14605.000  | 5643288.000 | 7970.746   |
| 467   | 0   | 418 | 0   | 49  | 0   | GET    | /api/player/player/.+                 | 0.004 | 14.340 | 772.724  | 1.655 | 6.972  | 7.812  | 14.148 | 3.130  | 0.000      | 2007.000   | 321125.000  | 687.634    |
| 55    | 0   | 48  | 0   | 7   | 0   | POST   | /api/organizer/competition/.+/score   | 0.204 | 14.140 | 163.749  | 2.977 | 7.332  | 8.724  | 14.140 | 2.801  | 0.000      | 62.000     | 2786.000    | 50.655     |
| 17    | 0   | 15  | 0   | 2   | 0   | GET    | /api/admin/tenants/billing            | 2.928 | 17.461 | 134.434  | 7.908 | 12.317 | 17.461 | 17.461 | 3.995  | 1470.000   | 1603.000   | 22800.000   | 1341.176   |
| 8     | 0   | 8   | 0   | 0   | 0   | POST   | /api/organizer/players/add            | 3.452 | 5.896  | 37.356   | 4.670 | 5.896  | 5.896  | 5.896  | 0.963  | 12150.000  | 24028.000  | 143171.000  | 17896.375  |
| 20    | 0   | 20  | 0   | 0   | 0   | GET    | /api/organizer/billing                | 0.005 | 1.896  | 4.905    | 0.245 | 1.064  | 1.324  | 1.896  | 0.520  | 320.000    | 5194.000   | 36988.000   | 1849.400   |
| 1     | 0   | 1   | 0   | 0   | 0   | POST   | /initialize                           | 3.452 | 3.452  | 3.452    | 3.452 | 3.452  | 3.452  | 3.452  | 0.000  | 55.000     | 55.000     | 55.000      | 55.000     |
| 106   | 0   | 95  | 0   | 11  | 0   | GET    | /api/player/competitions              | 0.004 | 0.060  | 1.180    | 0.011 | 0.024  | 0.028  | 0.032  | 0.010  | 39.000     | 2450.000   | 112792.000  | 1064.075   |
| 38    | 0   | 37  | 0   | 1   | 0   | POST   | /api/organizer/competitions/add       | 0.004 | 0.056  | 1.008    | 0.027 | 0.048  | 0.052  | 0.056  | 0.011  | 39.000     | 183.000    | 6014.000    | 158.263    |
| 11    | 0   | 6   | 0   | 5   | 0   | POST   | /api/admin/tenants/add                | 0.028 | 0.244  | 0.636    | 0.058 | 0.088  | 0.244  | 0.244  | 0.066  | 39.000     | 197.000    | 1323.000    | 120.273    |
| 34    | 0   | 33  | 0   | 1   | 0   | POST   | /api/organizer/competition/.+/finish  | 0.008 | 0.088  | 0.524    | 0.015 | 0.024  | 0.032  | 0.088  | 0.014  | 21.000     | 39.000     | 732.000     | 21.529     |
| 14    | 0   | 13  | 0   | 1   | 0   | POST   | /api/organizer/player/.+/disqualified | 0.004 | 0.044  | 0.192    | 0.014 | 0.016  | 0.044  | 0.044  | 0.009  | 39.000     | 162.000    | 2063.000    | 147.357    |
| 24    | 0   | 24  | 0   | 0   | 0   | GET    | /api/organizer/players                | 0.004 | 0.012  | 0.092    | 0.004 | 0.008  | 0.012  | 0.012  | 0.004  | 3897.000   | 71991.000  | 506204.000  | 21091.833  |
| 18    | 0   | 18  | 0   | 0   | 0   | GET    | /api/me                               | 0.000 | 0.016  | 0.024    | 0.001 | 0.004  | 0.016  | 0.016  | 0.004  | 173.000    | 196.000    | 3206.000    | 178.111    |
| 5     | 0   | 5   | 0   | 0   | 0   | GET    | /js/app.3a4ec98c.js                   | 0.000 | 0.009  | 0.017    | 0.003 | 0.009  | 0.009  | 0.009  | 0.004  | 33294.000  | 33294.000  | 166470.000  | 33294.000  |
| 2     | 0   | 2   | 0   | 0   | 0   | POST   | /auth/login/admin                     | 0.004 | 0.008  | 0.012    | 0.006 | 0.008  | 0.008  | 0.008  | 0.002  | 0.000      | 0.000      | 0.000       | 0.000      |
| 1     | 0   | 1   | 0   | 0   | 0   | GET    | /index.html                           | 0.000 | 0.000  | 0.000    | 0.000 | 0.000  | 0.000  | 0.000  | 0.000  | 479.000    | 479.000    | 479.000     | 479.000    |
| 6     | 0   | 6   | 0   | 0   | 0   | GET    | /favicon.ico                          | 0.000 | 0.000  | 0.000    | 0.000 | 0.000  | 0.000  | 0.000  | 0.000  | 4286.000   | 4286.000   | 25716.000   | 4286.000   |
| 4     | 0   | 4   | 0   | 0   | 0   | GET    | /img/isuports_light.svg               | 0.000 | 0.000  | 0.000    | 0.000 | 0.000  | 0.000  | 0.000  | 0.000  | 1748.000   | 1748.000   | 6992.000    | 1748.000   |
| 4     | 0   | 4   | 0   | 0   | 0   | GET    | /js/chunk-vendors.40ff55a3.js         | 0.000 | 0.000  | 0.000    | 0.000 | 0.000  | 0.000  | 0.000  | 0.000  | 131543.000 | 131543.000 | 526172.000  | 131543.000 |
| 5     | 0   | 5   | 0   | 0   | 0   | GET    | /css/app.83b4c321.css                 | 0.000 | 0.000  | 0.000    | 0.000 | 0.000  | 0.000  | 0.000  | 0.000  | 4868.000   | 4868.000   | 24340.000   | 4868.000   |
| 4     | 0   | 4   | 0   | 0   | 0   | GET    | /                                     | 0.000 | 0.000  | 0.000    | 0.000 | 0.000  | 0.000  | 0.000  | 0.000  | 479.000    | 479.000    | 1916.000    | 479.000    |
| 1     | 0   | 1   | 0   | 0   | 0   | GET    | /admin/                               | 0.000 | 0.000  | 0.000    | 0.000 | 0.000  | 0.000  | 0.000  | 0.000  | 479.000    | 479.000    | 479.000     | 479.000    |
| 1     | 0   | 1   | 0   | 0   | 0   | GET    | /api/organizer/competitions           | 0.000 | 0.000  | 0.000    | 0.000 | 0.000  | 0.000  | 0.000  | 0.000  | 177.000    | 177.000    | 177.000     | 177.000    |
+-------+-----+-----+-----+-----+-----+--------+---------------------------------------+-------+--------+----------+-------+--------+--------+--------+--------+------------+------------+-------------+------------+
vintersnowvintersnow

MySQLのslow query

を設定しようとしたが、/etc/mysql/mysql.conf.d/mysqld.cnf に追記するだけではなぜかだめだった。
SET PERSIST を使用して設定をした。

pt-query-digest

sudo apt-get install percona-toolkit
sudo pt-query-digest /var/log/mysql/slow.log > slow_query

ただ、今回は問題的にこれはほとんど使わなかった。

vintersnowvintersnow

pprof

go標準の分析ツールでどの関数にどれだけのcpu timeを消費しているのか分かる。重い関数が分かるので便利だが、cpu timeなので待機が多いと余り使えない。

import 	(
	_ "net/http/pprof"
	"runtime"
)

func main() {
	runtime.SetBlockProfileRate(1)
	runtime.SetMutexProfileFraction(1)

	// pprof
	go func() {
		log.Println(http.ListenAndServe("localhost:6060", nil))
	}()
}
❯ go tool pprof 'http://localhost:6060/debug/pprof/profile?seconds=10'
Fetching profile over HTTP from http://localhost:6060/debug/pprof/profile?seconds=10
Saved profile in /Users/vinter/pprof/pprof.samples.cpu.001.pb.gz
Type: cpu
Time: Jul 18, 2022 at 8:26pm (JST)
Duration: 10s, Total samples = 0
No samples were found with the default sample value type.
Try "sample_index" command to analyze different sample values.
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) %
go tool pprof -http=localhost:12345 ~/pprof/pprof.samples.cpu.001.pb.gz

portの開放がめんどうだったので、port forwardingをする

ssh -L 12345:isu1:12345
vintersnowvintersnow

問題設定

isuconのスコアボードを提供するマルチテナントSaaS
スコアボードが重い

vintersnowvintersnow

実装を見てみると、mysqlをほとんど使わずに、なんと各tenentごとsqliteに全データが格納されている。

これをmysqlに移行しようと思ったのだが、結構実装を変更する必要があり上にデータ量が多いため、移行や初期化に時間がかかりそうだった。というか掛って14時ぐらいまで時間を浪費していた。(データの移行でPCがhangした...)

結局あきらめて、sqliteのまま行くことにした。

vintersnowvintersnow

重い2つの/api/player/competition/.+/ranking/api/player/player/.+軽くする

N+1問題があったのでjoinして持ってくるようにした。

テーブルから集計をするために、テーブルごとに排他ロックをしていたので、読み込みはread lock
だけに変更した。
(解説を見れば、sqliteのtransactionを使えばよかったぽい。flock.RLOCKでshared lockを取るようにしていた)

更新系(/api/organizer/competition/.+/score)で、lockを取ってからcsvをパースをしていたので、順番を前後させた。

これで大体スコアが8000点になった。 (この時点で15:50)
面白いことに、最初の二つだけでは3000点のままだった。長時間の排他ロックが特に悪かったことが分かる。

+-------+-----+------+-----+-----+-----+--------+---------------------------------------+--------+--------+---------+--------+--------+--------+--------+--------+-----------+-----------+--------------+-----------+
| COUNT | 1XX | 2XX  | 3XX | 4XX | 5XX | METHOD |                  URI                  |  MIN   |  MAX   |   SUM   |  AVG   |  P90   |  P95   |  P99   | STDDEV | MIN(BODY) | MAX(BODY) |  SUM(BODY)   | AVG(BODY) |
+-------+-----+------+-----+-----+-----+--------+---------------------------------------+--------+--------+---------+--------+--------+--------+--------+--------+-----------+-----------+--------------+-----------+
| 63    | 0   | 56   | 0   | 7   | 0   | POST   | /api/organizer/competition/.+/score   | 0.004  | 12.896 | 137.488 | 2.182  | 5.584  | 5.996  | 12.896 | 2.281  | 0.000     | 62.000    | 3219.000     | 51.095    |
| 12    | 0   | 10   | 0   | 2   | 0   | GET    | /api/admin/tenants/billing            | 2.780  | 19.908 | 125.784 | 10.482 | 14.676 | 19.908 | 19.908 | 4.395  | 1573.000  | 1573.000  | 15268.000    | 1272.333  |
| 4720  | 0   | 4705 | 0   | 15  | 0   | GET    | /api/player/competition/.+/ranking    | 0.004  | 0.388  | 83.540  | 0.018  | 0.028  | 0.036  | 0.064  | 0.012  | 0.000     | 14851.000 | 15774020.000 | 3341.953  |
| 8     | 0   | 8    | 0   | 0   | 0   | POST   | /api/organizer/players/add            | 2.440  | 5.340  | 28.584  | 3.573  | 5.340  | 5.340  | 5.340  | 1.064  | 12150.000 | 26492.000 | 149219.000   | 18652.375 |
| 1     | 0   | 1    | 0   | 0   | 0   | POST   | /initialize                           | 19.292 | 19.292 | 19.292  | 19.292 | 19.292 | 19.292 | 19.292 | 0.000  | 55.000    | 55.000    | 55.000       | 55.000    |
| 24    | 0   | 24   | 0   | 0   | 0   | GET    | /api/organizer/billing                | 0.008  | 4.668  | 11.520  | 0.480  | 1.396  | 4.080  | 4.668  | 1.215  | 323.000   | 6378.000  | 52577.000    | 2190.708  |
| 1676  | 0   | 1663 | 0   | 13  | 0   | GET    | /api/player/player/.+                 | 0.004  | 0.112  | 10.116  | 0.006  | 0.012  | 0.016  | 0.024  | 0.006  | 39.000    | 2532.000  | 1769844.000  | 1055.993  |
| 438   | 0   | 425  | 0   | 13  | 0   | GET    | /api/player/competitions              | 0.004  | 0.020  | 1.996   | 0.005  | 0.012  | 0.012  | 0.020  | 0.004  | 39.000    | 3100.000  | 482652.000   | 1101.945  |
| 44    | 0   | 43   | 0   | 1   | 0   | POST   | /api/organizer/competitions/add       | 0.012  | 0.120  | 1.388   | 0.032  | 0.048  | 0.068  | 0.120  | 0.020  | 39.000    | 192.000   | 7274.000     | 165.318   |
| 11    | 0   | 6    | 0   | 5   | 0   | POST   | /api/admin/tenants/add                | 0.004  | 0.260  | 0.720   | 0.065  | 0.256  | 0.260  | 0.260  | 0.093  | 39.000    | 198.000   | 1292.000     | 117.455   |
| 40    | 0   | 39   | 0   | 1   | 0   | POST   | /api/organizer/competition/.+/finish  | 0.004  | 0.020  | 0.396   | 0.010  | 0.012  | 0.016  | 0.020  | 0.004  | 21.000    | 39.000    | 858.000      | 21.450    |
| 16    | 0   | 15   | 0   | 1   | 0   | POST   | /api/organizer/player/.+/disqualified | 0.004  | 0.024  | 0.148   | 0.009  | 0.016  | 0.024  | 0.024  | 0.005  | 39.000    | 163.000   | 2381.000     | 148.812   |
| 28    | 0   | 28   | 0   | 0   | 0   | GET    | /api/organizer/players                | 0.004  | 0.020  | 0.136   | 0.005  | 0.012  | 0.012  | 0.020  | 0.004  | 4395.000  | 75476.000 | 641229.000   | 22901.036 |
| 1     | 0   | 1    | 0   | 0   | 0   | GET    | /css/app.83b4c321.css                 | 0.000  | 0.000  | 0.000   | 0.000  | 0.000  | 0.000  | 0.000  | 0.000  | 4868.000  | 4868.000  | 4868.000     | 4868.000  |
| 1     | 0   | 1    | 0   | 0   | 0   | GET    | /js/app.3a4ec98c.js                   | 0.000  | 0.000  | 0.000   | 0.000  | 0.000  | 0.000  | 0.000  | 0.000  | 33294.000 | 33294.000 | 33294.000    | 33294.000 |
| 1     | 0   | 1    | 0   | 0   | 0   | GET    | /api/organizer/competitions           | 0.000  | 0.000  | 0.000   | 0.000  | 0.000  | 0.000  | 0.000  | 0.000  | 177.000   | 177.000   | 177.000      | 177.000   |
| 1     | 0   | 1    | 0   | 0   | 0   | GET    | /index.html                           | 0.000  | 0.000  | 0.000   | 0.000  | 0.000  | 0.000  | 0.000  | 0.000  | 479.000   | 479.000   | 479.000      | 479.000   |
| 1     | 0   | 0    | 1   | 0   | 0   | GET    | /admin/                               | 0.000  | 0.000  | 0.000   | 0.000  | 0.000  | 0.000  | 0.000  | 0.000  | 0.000     | 0.000     | 0.000        | 0.000     |
| 3     | 0   | 3    | 0   | 0   | 0   | GET    | /api/me                               | 0.000  | 0.000  | 0.000   | 0.000  | 0.000  | 0.000  | 0.000  | 0.000  | 173.000   | 173.000   | 519.000      | 173.000   |
+-------+-----+------+-----+-----+-----+--------+---------------------------------------+--------+--------+---------+--------+--------+--------+--------+--------+-----------+-----------+--------------+-----------+
vintersnowvintersnow

まだ、/api/organizer/competition/.+/score が重いので、中身を確認する。
idを生成するためにmysqlに毎回インサートをしてidをインクリメントしていた。
insertのたびに発生する作業なので、id生成をアプリケーション側でするようにする。
sqliteのindexがどのような仕組みなのか知らなかったので、今回はuuidやulidより文字数が少ないnanoidを使用してみた。
また、insertもbulk insertに変更した。

意図通り、/api/organizer/competition/.+/scoreは高速化されたが、逆に/api/player/competition/.+/ranking/api/player/player/.+が遅くなった。ままならない...

これで10242点 。大台に乗れた。
(この時点で17:00になっていた)

+-------+-----+------+-----+-----+-----+--------+---------------------------------------+-------+-------+---------+-------+-------+-------+-------+--------+-----------+------------+--------------+-----------+
| COUNT | 1XX | 2XX  | 3XX | 4XX | 5XX | METHOD |                  URI                  |  MIN  |  MAX  |   SUM   |  AVG  |  P90  |  P95  |  P99  | STDDEV | MIN(BODY) | MAX(BODY)  |  SUM(BODY)   | AVG(BODY) |
+-------+-----+------+-----+-----+-----+--------+---------------------------------------+-------+-------+---------+-------+-------+-------+-------+--------+-----------+------------+--------------+-----------+
| 5099  | 0   | 5078 | 0   | 21  | 0   | GET    | /api/player/player/.+                 | 0.008 | 2.388 | 363.856 | 0.071 | 0.068 | 0.500 | 1.400 | 0.250  | 0.000     | 3027.000   | 8795768.000  | 1724.999  |
| 2599  | 0   | 2578 | 0   | 21  | 0   | GET    | /api/player/competition/.+/ranking    | 0.008 | 2.420 | 212.600 | 0.082 | 0.140 | 0.320 | 1.352 | 0.230  | 0.000     | 15796.000  | 33129576.000 | 12747.047 |
| 18    | 0   | 16   | 0   | 2   | 0   | GET    | /api/admin/tenants/billing            | 2.700 | 9.716 | 110.600 | 6.144 | 9.440 | 9.716 | 9.716 | 2.069  | 0.000     | 1569.000   | 24225.000    | 1345.833  |
| 18    | 0   | 18   | 0   | 0   | 0   | POST   | /api/organizer/players/add            | 0.940 | 2.372 | 26.888  | 1.494 | 2.248 | 2.372 | 2.372 | 0.431  | 13450.000 | 29810.000  | 415284.000   | 23071.333 |
| 57    | 0   | 56   | 0   | 1   | 0   | GET    | /api/organizer/billing                | 0.004 | 1.508 | 16.096  | 0.282 | 1.008 | 1.200 | 1.508 | 0.409  | 0.000     | 7843.000   | 201590.000   | 3536.667  |
| 332   | 0   | 313  | 0   | 19  | 0   | GET    | /api/player/competitions              | 0.016 | 0.312 | 7.712   | 0.023 | 0.044 | 0.080 | 0.212 | 0.034  | 39.000    | 3853.000   | 669098.000   | 2015.355  |
| 109   | 0   | 103  | 0   | 5   | 1   | POST   | /api/organizer/competition/.+/score   | 0.004 | 0.352 | 4.984   | 0.046 | 0.116 | 0.160 | 0.292 | 0.058  | 39.000    | 62.000     | 5786.000     | 53.083    |
| 1     | 0   | 1    | 0   | 0   | 0   | POST   | /initialize                           | 3.316 | 3.316 | 3.316   | 3.316 | 3.316 | 3.316 | 3.316 | 0.000  | 55.000    | 55.000     | 55.000       | 55.000    |
| 84    | 0   | 83   | 0   | 1   | 0   | POST   | /api/organizer/competitions/add       | 0.004 | 0.120 | 1.448   | 0.017 | 0.028 | 0.068 | 0.120 | 0.022  | 39.000    | 203.000    | 14779.000    | 175.940   |
| 64    | 0   | 64   | 0   | 0   | 0   | GET    | /api/organizer/players                | 0.004 | 0.300 | 1.252   | 0.020 | 0.028 | 0.104 | 0.300 | 0.050  | 2942.000  | 304417.000 | 3114281.000  | 48660.641 |
| 81    | 0   | 80   | 0   | 1   | 0   | POST   | /api/organizer/competition/.+/finish  | 0.004 | 0.172 | 1.052   | 0.013 | 0.016 | 0.024 | 0.172 | 0.019  | 21.000    | 39.000     | 1719.000     | 21.222    |
| 12    | 0   | 7    | 0   | 5   | 0   | POST   | /api/admin/tenants/add                | 0.036 | 0.068 | 0.340   | 0.028 | 0.056 | 0.068 | 0.068 | 0.024  | 39.000    | 194.000    | 1489.000     | 124.083   |
| 22    | 0   | 21   | 0   | 1   | 0   | POST   | /api/organizer/player/.+/disqualified | 0.004 | 0.016 | 0.220   | 0.010 | 0.012 | 0.016 | 0.016 | 0.003  | 39.000    | 170.000    | 3351.000     | 152.318   |
| 1     | 0   | 1    | 0   | 0   | 0   | GET    | /api/organizer/competitions           | 0.004 | 0.004 | 0.004   | 0.004 | 0.004 | 0.004 | 0.004 | 0.000  | 190.000   | 190.000    | 190.000      | 190.000   |
| 1     | 0   | 1    | 0   | 0   | 0   | GET    | /css/app.83b4c321.css                 | 0.000 | 0.000 | 0.000   | 0.000 | 0.000 | 0.000 | 0.000 | 0.000  | 4868.000  | 4868.000   | 4868.000     | 4868.000  |
| 1     | 0   | 1    | 0   | 0   | 0   | GET    | /js/app.3a4ec98c.js                   | 0.000 | 0.000 | 0.000   | 0.000 | 0.000 | 0.000 | 0.000 | 0.000  | 33294.000 | 33294.000  | 33294.000    | 33294.000 |
| 1     | 0   | 1    | 0   | 0   | 0   | GET    | /index.html                           | 0.000 | 0.000 | 0.000   | 0.000 | 0.000 | 0.000 | 0.000 | 0.000  | 479.000   | 479.000    | 479.000      | 479.000   |
+-------+-----+------+-----+-----+-----+--------+---------------------------------------+-------+-------+---------+-------+-------+-------+-------+--------+-----------+------------+--------------+-----------+
vintersnowvintersnow

sqliteを使用するときに毎回openするのがもったいないのでコネクションを使い回すように変更した。
tenentIdをkeyにmapで保持するだけで済むのがgoの良い点。
sqliteのファイルは初期化時に上書きされるので、初期化時にコネクションにキャッシュを消す必要がある(1敗)

スコアは大体11000点 (この時点で17:30?)

+-------+-----+------+-----+-----+-----+--------+---------------------------------------+-------+--------+---------+-------+-------+--------+--------+--------+-----------+------------+--------------+-----------+
| COUNT | 1XX | 2XX  | 3XX | 4XX | 5XX | METHOD |                  URI                  |  MIN  |  MAX   |   SUM   |  AVG  |  P90  |  P95   |  P99   | STDDEV | MIN(BODY) | MAX(BODY)  |  SUM(BODY)   | AVG(BODY) |
+-------+-----+------+-----+-----+-----+--------+---------------------------------------+-------+--------+---------+-------+-------+--------+--------+--------+-----------+------------+--------------+-----------+
| 4897  | 0   | 4876 | 0   | 21  | 0   | GET    | /api/player/player/.+                 | 0.004 | 2.860  | 361.400 | 0.074 | 0.092 | 0.312  | 1.528  | 0.265  | 0.000     | 3023.000   | 8446720.000  | 1724.876  |
| 2670  | 0   | 2646 | 0   | 24  | 0   | GET    | /api/player/competition/.+/ranking    | 0.004 | 2.580  | 227.200 | 0.085 | 0.144 | 0.316  | 1.400  | 0.235  | 0.000     | 15792.000  | 32338398.000 | 12111.760 |
| 18    | 0   | 17   | 0   | 1   | 0   | GET    | /api/admin/tenants/billing            | 2.592 | 11.668 | 109.304 | 6.072 | 7.728 | 11.668 | 11.668 | 2.084  | 0.000     | 1548.000   | 25822.000    | 1434.556  |
| 16    | 0   | 16   | 0   | 0   | 0   | POST   | /api/organizer/players/add            | 1.164 | 2.680  | 25.748  | 1.609 | 2.004 | 2.680  | 2.680  | 0.389  | 13450.000 | 29060.000  | 366664.000   | 22916.500 |
| 58    | 0   | 56   | 0   | 2   | 0   | GET    | /api/organizer/billing                | 0.004 | 1.828  | 18.176  | 0.313 | 1.352 | 1.696  | 1.828  | 0.497  | 0.000     | 7836.000   | 203777.000   | 3513.397  |
| 323   | 0   | 304  | 0   | 19  | 0   | GET    | /api/player/competitions              | 0.028 | 0.272  | 8.152   | 0.025 | 0.056 | 0.084  | 0.164  | 0.032  | 39.000    | 3849.000   | 612683.000   | 1896.851  |
| 109   | 0   | 104  | 0   | 5   | 0   | POST   | /api/organizer/competition/.+/score   | 0.008 | 0.344  | 5.368   | 0.049 | 0.092 | 0.268  | 0.328  | 0.068  | 39.000    | 62.000     | 5800.000     | 53.211    |
| 1     | 0   | 1    | 0   | 0   | 0   | POST   | /initialize                           | 3.384 | 3.384  | 3.384   | 3.384 | 3.384 | 3.384  | 3.384  | 0.000  | 55.000    | 55.000     | 55.000       | 55.000    |
| 64    | 0   | 64   | 0   | 0   | 0   | GET    | /api/organizer/players                | 0.004 | 0.400  | 2.136   | 0.033 | 0.112 | 0.276  | 0.400  | 0.085  | 3088.000  | 295296.000 | 2958381.000  | 46224.703 |
| 84    | 0   | 83   | 0   | 1   | 0   | POST   | /api/organizer/competitions/add       | 0.004 | 0.276  | 1.924   | 0.023 | 0.040 | 0.084  | 0.276  | 0.038  | 39.000    | 204.000    | 14737.000    | 175.440   |
| 82    | 0   | 81   | 0   | 1   | 0   | POST   | /api/organizer/competition/.+/finish  | 0.004 | 0.100  | 1.132   | 0.014 | 0.028 | 0.036  | 0.100  | 0.013  | 21.000    | 39.000     | 1740.000     | 21.220    |
| 11    | 0   | 6    | 0   | 5   | 0   | POST   | /api/admin/tenants/add                | 0.004 | 0.084  | 0.368   | 0.033 | 0.068 | 0.084  | 0.084  | 0.030  | 39.000    | 189.000    | 1282.000     | 116.545   |
| 22    | 0   | 21   | 0   | 1   | 0   | POST   | /api/organizer/player/.+/disqualified | 0.004 | 0.036  | 0.240   | 0.011 | 0.016 | 0.024  | 0.036  | 0.007  | 39.000    | 170.000    | 3356.000     | 152.545   |
| 8     | 0   | 8    | 0   | 0   | 0   | GET    | /api/me                               | 0.000 | 0.004  | 0.004   | 0.001 | 0.004 | 0.004  | 0.004  | 0.001  | 173.000   | 265.000    | 1568.000     | 196.000   |
| 3     | 0   | 0    | 3   | 0   | 0   | GET    | /                                     | 0.000 | 0.000  | 0.000   | 0.000 | 0.000 | 0.000  | 0.000  | 0.000  | 0.000     | 0.000      | 0.000        | 0.000     |
| 1     | 0   | 1    | 0   | 0   | 0   | GET    | /css/app.83b4c321.css                 | 0.000 | 0.000  | 0.000   | 0.000 | 0.000 | 0.000  | 0.000  | 0.000  | 4868.000  | 4868.000   | 4868.000     | 4868.000  |
| 1     | 0   | 1    | 0   | 0   | 0   | GET    | /js/app.3a4ec98c.js                   | 0.000 | 0.000  | 0.000   | 0.000 | 0.000 | 0.000  | 0.000  | 0.000  | 33294.000 | 33294.000  | 33294.000    | 33294.000 |
| 1     | 0   | 1    | 0   | 0   | 0   | GET    | /index.html                           | 0.000 | 0.000  | 0.000   | 0.000 | 0.000 | 0.000  | 0.000  | 0.000  | 479.000   | 479.000    | 479.000      | 479.000   |
| 1     | 0   | 0    | 0   | 1   | 0   | GET    | /bapi/fiat/v1/public/fiatpayment/menu | 0.000 | 0.000  | 0.000   | 0.000 | 0.000 | 0.000  | 0.000  | 0.000  | 162.000   | 162.000    | 162.000      | 162.000   |
| 1     | 0   | 0    | 1   | 0   | 0   | GET    | /competition/62ccaec32                | 0.000 | 0.000  | 0.000   | 0.000 | 0.000 | 0.000  | 0.000  | 0.000  | 0.000     | 0.000      | 0.000        | 0.000     |
| 1     | 0   | 1    | 0   | 0   | 0   | GET    | /api/organizer/competitions           | 0.000 | 0.000  | 0.000   | 0.000 | 0.000 | 0.000  | 0.000  | 0.000  | 190.000   | 190.000    | 190.000      | 190.000   |
| 1     | 0   | 0    | 0   | 1   | 0   | GET    | /api/v3/order                         | 0.000 | 0.000  | 0.000   | 0.000 | 0.000 | 0.000  | 0.000  | 0.000  | 162.000   | 162.000    | 162.000      | 162.000   |
+-------+-----+------+-----+-----+-----+--------+---------------------------------------+-------+--------+---------+-------+-------+--------+--------+--------+-----------+------------+--------------+-----------+
vintersnowvintersnow

/api/organizer/players/addのbody sizeが大きく、minの時間も長い。
bulk insertに変更してみた

これで大体12000点 (17:45)

vintersnowvintersnow

最後にmysqlとnginxのログを無効化して、pprofも削除した。

13000 ~ 14000ぐらいでフィニッシュ

vintersnowvintersnow

よかった点
出遅れたものの、最後はスコアを延せた。
事前に予行練習をしたため、デプロイスクリプトを準備したり、ログ出力周りがスムーズに出来たのもよかった。isucon本ありがたや

vintersnowvintersnow

反省点
MySQLの移行を早めにあきらめるべきだった。やり始める時からコスト的に本当か?という疑問はあったが、常識的にそうした方が良いと思い始めてしまった。結局mysql移行したとしてもそんなに武器を持っていないのでちゃんと高速化出来たのか怪しい。
最近web界隈でsqliteが話題になったので、その速度や安定性は知っていたのだが、常識や定石に囚われてしまった。常識を疑え。

今回ほとんどサービスのことを理解出来なかったし、ローカルでの実行環境もなかったので、結構勢いで開発をしていた。本当はミニベンチがあった方が安定していたのだろうな。

いくつか重そうだなと思いつつ手をつけれなかったことがあった(jwtの認証など)。時間がなかったというもの大きいが、ボトルネックの確証がなかったという理由もある。これらをボトルネックだと判断するにはなにを測定して判断するのかまだ分からず、勉強不足を感じた。

goは殆ど書いたことなかったので、基礎ぐらいは勉強していくべきだった。競技中に変数宣言の仕方やfor文を調べるのはさすがに恥しくなった。

このスクラップは2023/11/14にクローズされました