Closed19

ISUCON10予選

myuonmyuon

ami-03bbe60df80bdccc0 をt2.microで4台起動する
それぞれisucon1, 2, 3, bと名前をつけておく

myuonmyuon

~/.ssh/config を設定してsshで中に入れるようにした

myuonmyuon

設定が諸々動いたので、とりあえず初回ベンチ

nginxを何も設定していないので、isucon1に向けて投げてみる

2023/10/15 07:10:04 bench.go:78: === initialize ===
2023/10/15 07:10:16 bench.go:90: === verify ===
2023/10/15 07:10:18 bench.go:100: === validation ===
2023/10/15 07:11:18 bench.go:102: 最終的な負荷レベル: 0
{"pass":true,"score":235,"messages":[],"reason":"OK","language":"go"}
myuonmyuon

クロスコンパイルを忘れて無限にハマっていたが、とりあえずMakefileを書いてソースコードのpullとアプリのdeployはできるようになった。

サーバーログはjournalctlで見れる

journalctl -u isuumo.go.service | tail
myuonmyuon

Kataribeを入れてみる
goが1.14でビビる。バージョンアップしてもよいが、特定のサーバーの環境だけ変わって変なことになったら嫌なので頑張っていれる

myuonmyuon

kataribeを読む限り以下のAPIが重そうなのでコードを少し読んでみる。

POST /api/estate/nazotte
GET /api/chair/low_price
GET /api/estate/low_priced
myuonmyuon

現時点でのスコア

2023/10/15 08:43:36 bench.go:78: === initialize ===
2023/10/15 08:43:39 bench.go:90: === verify ===
2023/10/15 08:43:40 bench.go:100: === validation ===
2023/10/15 08:44:21 load.go:181: 負荷レベルが上昇しました。
2023/10/15 08:44:24 fails.go:105: [client.(*Client).SearchEstatesNazotte] /home/isucon/isuumo/bench/client/webapp.go:367
    message("POST /api/estate/nazotte: リクエストに失敗しました")
[client.(*Client).Do] /home/isucon/isuumo/bench/client/client.go:136
    code(error timeout)
    *url.Error("Post \"http://172.31.22.42/api/estate/nazotte\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)")
    *http.httpError("context deadline exceeded (Client.Timeout exceeded while awaiting headers)")
[CallStack]
    [client.(*Client).Do] /home/isucon/isuumo/bench/client/client.go:136
    [client.(*Client).SearchEstatesNazotte] /home/isucon/isuumo/bench/client/webapp.go:361
    [scenario.estateNazotteSearchScenario] /home/isucon/isuumo/bench/scenario/estateNazotteSearchScenario.go:214
    [scenario.runEstateNazotteSearchWorker] /home/isucon/isuumo/bench/scenario/load.go:100
    [runtime.goexit] /home/isucon/local/go/src/runtime/asm_amd64.s:1373
2023/10/15 08:44:27 fails.go:105: [client.(*Client).SearchEstatesNazotte] /home/isucon/isuumo/bench/client/webapp.go:367
    message("POST /api/estate/nazotte: リクエストに失敗しました")
[client.(*Client).Do] /home/isucon/isuumo/bench/client/client.go:136
    code(error timeout)
    *url.Error("Post \"http://172.31.22.42/api/estate/nazotte\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)")
    *http.httpError("context deadline exceeded (Client.Timeout exceeded while awaiting headers)")
[CallStack]
    [client.(*Client).Do] /home/isucon/isuumo/bench/client/client.go:136
    [client.(*Client).SearchEstatesNazotte] /home/isucon/isuumo/bench/client/webapp.go:361
    [scenario.estateNazotteSearchScenario] /home/isucon/isuumo/bench/scenario/estateNazotteSearchScenario.go:214
    [scenario.runEstateNazotteSearchWorker] /home/isucon/isuumo/bench/scenario/load.go:100
    [runtime.goexit] /home/isucon/local/go/src/runtime/asm_amd64.s:1373
2023/10/15 08:44:34 fails.go:105: [client.(*Client).SearchEstatesNazotte] /home/isucon/isuumo/bench/client/webapp.go:367
    message("POST /api/estate/nazotte: リクエストに失敗しました")
[client.(*Client).Do] /home/isucon/isuumo/bench/client/client.go:136
    code(error timeout)
    *url.Error("Post \"http://172.31.22.42/api/estate/nazotte\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)")
    *http.httpError("context deadline exceeded (Client.Timeout exceeded while awaiting headers)")
[CallStack]
    [client.(*Client).Do] /home/isucon/isuumo/bench/client/client.go:136
    [client.(*Client).SearchEstatesNazotte] /home/isucon/isuumo/bench/client/webapp.go:361
    [scenario.estateNazotteSearchScenario] /home/isucon/isuumo/bench/scenario/estateNazotteSearchScenario.go:214
    [scenario.runEstateNazotteSearchWorker] /home/isucon/isuumo/bench/scenario/load.go:100
    [runtime.goexit] /home/isucon/local/go/src/runtime/asm_amd64.s:1373
2023/10/15 08:44:34 fails.go:105: [client.(*Client).SearchEstatesNazotte] /home/isucon/isuumo/bench/client/webapp.go:367
    message("POST /api/estate/nazotte: リクエストに失敗しました")
[client.(*Client).Do] /home/isucon/isuumo/bench/client/client.go:136
    code(error timeout)
    *url.Error("Post \"http://172.31.22.42/api/estate/nazotte\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)")
    *http.httpError("context deadline exceeded (Client.Timeout exceeded while awaiting headers)")
[CallStack]
    [client.(*Client).Do] /home/isucon/isuumo/bench/client/client.go:136
    [client.(*Client).SearchEstatesNazotte] /home/isucon/isuumo/bench/client/webapp.go:361
    [scenario.estateNazotteSearchScenario] /home/isucon/isuumo/bench/scenario/estateNazotteSearchScenario.go:214
    [scenario.runEstateNazotteSearchWorker] /home/isucon/isuumo/bench/scenario/load.go:100
    [runtime.goexit] /home/isucon/local/go/src/runtime/asm_amd64.s:1373
2023/10/15 08:44:37 fails.go:105: [client.(*Client).SearchEstatesNazotte] /home/isucon/isuumo/bench/client/webapp.go:367
    message("POST /api/estate/nazotte: リクエストに失敗しました")
[client.(*Client).Do] /home/isucon/isuumo/bench/client/client.go:136
    code(error timeout)
    *url.Error("Post \"http://172.31.22.42/api/estate/nazotte\": context deadline exceeded (Client.Timeout exceeded while awaiting headers)")
    *http.httpError("context deadline exceeded (Client.Timeout exceeded while awaiting headers)")
[CallStack]
    [client.(*Client).Do] /home/isucon/isuumo/bench/client/client.go:136
    [client.(*Client).SearchEstatesNazotte] /home/isucon/isuumo/bench/client/webapp.go:361
    [scenario.estateNazotteSearchScenario] /home/isucon/isuumo/bench/scenario/estateNazotteSearchScenario.go:214
    [scenario.runEstateNazotteSearchWorker] /home/isucon/isuumo/bench/scenario/load.go:100
    [runtime.goexit] /home/isucon/local/go/src/runtime/asm_amd64.s:1373
2023/10/15 08:44:40 bench.go:102: 最終的な負荷レベル: 1
{"pass":true,"score":423,"messages":[{"text":"POST /api/estate/nazotte: リクエストに失敗しました (タイムアウトしました)","count":5}],"reason":"OK","language":"go"}
myuonmyuon

indexを貼ってみたが、スコア自体はほぼ変わらず。
P50の値自体は半分くらいになったので、負荷は軽くなっていそう。

リクエストに色々失敗しているのが気になっている。
とりあえずbotを弾くのをやるか〜となった

myuonmyuon

nginxでbotを弾くようにした。
スコアは微増なので、やはりまじめにAPIをどうにかしないといけないことがわかる。

myuonmyuon

よく見たらあんまりUPDATEしてなそうなのと、low_pricedあたりは丸ごとキャッシュして良さげなのでそれをやってみる。
特にestateは全く更新していないので、全部オンメモリで良い。

この辺りをやったらスコアもそれなりに伸びた。

myuonmyuon

low_pricedをキャッシュしたため、Totalでみるとなぞって検索が他の4倍くらいかかっているのでここがボトルネックになってきた。
とりあえずロジックを根本的に変更しないのであれば、app serverは増やした方が良さそうに見える。

また、nazotteはestateに依存しておりこいつは更新がないので例えばsqliteに入れてそれぞれのapp server内で完結させるとか、あるいはそもそもライブラリで完全に計算が可能ならその方が良さげ。

myuonmyuon

地味にリクエストがあるsearchEstates, searchChairsを眺めた。
searchEstatesはrentRangeIdを指定した検索が多く、そもそもrentはrangeIdに事前に計算して置けると思うのでそれをやりたいが、initializeのタイミングやpostEstateのタイミングがよくわからないので難しい。

データを投げ込んで良いタイミングがあったらそこで事前に計算しておく、というのをやりたい。
あるいは単にrentでindex貼ってもあまり変わらないか?

myuonmyuon

とりあえずappx2, dbx1台構成へ変更。
bind-addressが必要とかuser:isuconに権限足りなかったりを直しつつ再起動して再度ベンチ。
(ロードバランスの設定時でまあまあハマった)

スコアは850くらいになった

myuonmyuon

nazotte searchのところがどうしてもこれ以上良い方法が思いつかないので諦めることに。
諸々調整して最終スコアは882。

myuonmyuon

割とMySQLインスタンスが悲鳴を上げていたが、そこがどうもできなかったのが心残り。

myuonmyuon

後片付け

  • インスタンスを落とす
  • 今回用に作ったsecurity groupを消す
  • キーペアを削除
  • .ssh/configを元に戻す

Makefile

clean:
	rm -rf sv1/ sv2/ sv3/

cp-isuumo-1:
	mkdir -p sv1/webapp
	scp -r isucon1:/home/isucon/isuumo/webapp/go sv1/webapp/
	scp -r isucon1:/home/isucon/isuumo/webapp/nginx sv1/webapp/
	scp -r isucon1:/home/isucon/isuumo/webapp/mysql sv1/webapp/

build-sv1:
	cd sv1/webapp/go && make

deploy-sv1:
	scp sv1/webapp/go/isuumo isucon1:/tmp/isuumo
	scp -r sv1/webapp/mysql isucon1:/home/isucon/isuumo/webapp

	ssh isucon1 "sudo systemctl stop isuumo.go.service && sudo cp /tmp/isuumo /home/isucon/isuumo/webapp/go/isuumo && sudo systemctl start isuumo.go.service"

deploy-sv2:
	scp sv1/webapp/go/isuumo isucon2:/tmp/isuumo
	scp -r sv1/webapp/mysql isucon2:/home/isucon/isuumo/webapp

	ssh isucon2 "sudo systemctl stop isuumo.go.service && sudo cp /tmp/isuumo /home/isucon/isuumo/webapp/go/isuumo && sudo systemctl start isuumo.go.service"

deploy-sv3:
	scp sv1/webapp/go/isuumo isucon3:/tmp/isuumo
	scp -r sv1/webapp/mysql isucon3:/home/isucon/isuumo/webapp

	ssh isucon3 "sudo systemctl stop isuumo.go.service && sudo cp /tmp/isuumo /home/isucon/isuumo/webapp/go/isuumo && sudo systemctl start isuumo.go.service"

deploy-app: deploy-sv1 deploy-sv2 deploy-sv3

deploy-nginx-sv1:
	scp sv1/nginx.conf isucon1:/tmp/nginx.conf

	ssh isucon1 "sudo cp /tmp/nginx.conf /etc/nginx/nginx.conf && sudo systemctl restart nginx"

deploy-nginx-sv2:
	scp sv1/nginx.conf isucon2:/tmp/nginx.conf

	ssh isucon2 "sudo cp /tmp/nginx.conf /etc/nginx/nginx.conf && sudo systemctl restart nginx"

deploy-nginx-sv3:
	scp sv1/nginx.conf isucon3:/tmp/nginx.conf

	ssh isucon3 "sudo cp /tmp/nginx.conf /etc/nginx/nginx.conf && sudo systemctl restart nginx"

deploy-nginx: deploy-nginx-sv1 deploy-nginx-sv2

deploy-mysql-sv3:
	scp sv1/my.cnf isucon3:/tmp/my.cnf

	ssh isucon3 "sudo cp /tmp/my.cnf /etc/mysql/my.cnf && sudo systemctl restart mysql"

deploy-conf: deploy-nginx deploy-mysql-sv3

truncate-logs-sv1:
	ssh isucon1 "sudo rm /var/log/nginx/access.log && sudo systemctl reload nginx"

truncate-logs-sv2:
	ssh isucon2 "sudo rm /var/log/nginx/access.log && sudo systemctl reload nginx"

truncate-logs-sv3:
	ssh isucon3 "sudo rm /tmp/slow.log && sudo systemctl restart mysql"

truncate-logs: truncate-logs-sv1 truncate-logs-sv2 truncate-logs-sv3

.ssh/config

Host isucon1
  HostName 35.77.36.29
  User isucon
  IdentityFile ~/Downloads/isucon10-qualify.pem

Host isucon2
  HostName 54.92.121.174
  User isucon
  IdentityFile ~/Downloads/isucon10-qualify.pem

Host isucon3
  HostName 43.206.92.145
  User isucon
  IdentityFile ~/Downloads/isucon10-qualify.pem

Host isuconb
  HostName 35.74.239.84
  User isucon
  IdentityFile ~/Downloads/isucon10-qualify.pem
myuonmyuon

https://isucon.net/archives/55025156.html

毎回 popularity が降順 (DESC) であり、 ID の昇順 (ASC) で検索をしています。降順と昇順が組み合わさった ORDER BY は デフォルトでインストールされてる MySQL 5.7 では単純に index を貼っても効きません。

なるほどね、これはわかってなかった

あと、MySQLにgenerated columnとかあるんだなー初めて知った。

myuonmyuon

テーブルごとにDB分けるのはなかなか思いつかないので思いつきたい(実際DBで律速していたのでそっちの方が良さそう)

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