Open11

ISUCON練習メモ

kazuphkazuph

手元のM1 Macでprivate-isuの実行

Multipassのインストール

https://multipass.run/
これは手動で。

サーバー構築

git clone https://github.com/matsuu/cloud-init-isucon.git
cd cloud-init-isucon/private-isu

multipass launch --name private-isu --cpus 2 --disk 16G --mem 4G --cloud-init standalone.cfg 20.04

sshでログイン

multipass shell private-isu

手元で普通にsshしたい場合

sudo install -m 600 -o $USER -g $(id -g) /var/root/Library/Application\ Support/multipassd/ssh-keys/id_rsa ~/.ssh/multipass.id_rsa

.ssh/config

Host isucon
  Hostname <インスタンスのIP(後述)>
  User ubuntu
  IdentityFile ~/.ssh/multipass.id_rsa

IPは multipass list でわかる(もしくはログインした状態で hostname -I)。

$ multipass list                                                              main
Name                    State             IPv4             Image
private-isu             Running           192.168.64.12    Ubuntu 20.04 LTS

環境構築のログの確認

sudo tail -f /var/log/cloud-init-output.log

ベンチマーカの実行

cd /home/isucon/private_isu.git/benchmarker
./bin/benchmarker -u ./userdata -t http://$(hostname -I | perl -alE "say @F[0]")/
kazuphkazuph

isucon userでsshできるようにする

whoami ubuntu
sudo cp -r .ssh /home/isucon/
sudo chown -R isucon.isucon /home/isucon/.ssh
sudo chmod 700 /home/isucon/.ssh
sudo chmod 600 /home/isucon/.ssh/authorized_keys
exit

ssh isucon@isucon
kazuphkazuph

isucon 12でbenchの実行

cd bench
./bench -target-addr 127.0.0.1:443

初期スコア(M1 Pro 8コア)

SCORE: 3180

そのまま2回目

SCORE: 2346

え!?

multipasshが3つCPU100%なのが気になる…。

→その後Mac再起動して試したら4000台後半で安定。

kazuphkazuph

nginxのaccess logをjsonにする

sudo vi /etc/nginx/nginx.conf
...
log_format json escape=json '{"time":"$time_iso8601",'
                             '"host":"$remote_addr",'
                             '"port":$remote_port,'
                             '"method":"$request_method",'
                             '"uri":"$request_uri",'
                             '"status":"$status",'
                             '"body_bytes":$body_bytes_sent,'
                             '"referer":"$http_referer",'
                             '"ua":"$http_user_agent",'
                             '"request_time":"$request_time",'
                             '"response_time":"$upstream_response_time"}';

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

sudo systemctl reload nginx
sudo tail -f /var/log/nginx/access.log
kazuphkazuph

alpの導入

# Install Go
sudo apt install -y golang-go

# Install alp
git clone https://github.com/tkuchiki/alp.git
cd alp/cli/alp
go build -o alp
sudo mv alp /usr/local/bin/alp
alp
kazuphkazuph

alpの実行

# ログの退避
LOG_DATE=$(date +%Y%m%d%H%M)
echo $LOG_DATE
sudo mv /var/log/nginx/access.log /tmp/access_$LOG_DATE.log
sudo chown root:root /tmp/access_$LOG_DATE.log
sudo chmod 644 /tmp/access_$LOG_DATE.log
sudo systemctl reload nginx

# 集計 
sudo alp json --sort sum -r -m "/api/player/competition/[\w-]+/ranking,/api/player/player/[\w-]+,/api/organizer/competition/[\w-]+,/api/organizer/player/[\w-]+/disqualified,/image/\d+" --percentiles="50,90,95,99"  -o count,1xx,2xx,3xx,4xx,5xx,method,uri,min,max,sum,avg,p50,p90,p95,p99,stddev < /tmp/access_$LOG_DATE.log > /tmp/alp_$LOG_DATE.log
cat /tmp/alp_$LOG_DATE.log

# 結果
+-------+-----+-----+-----+-----+-----+--------+-------------------------------------------+-------+--------+----------+-------+-------+-------+--------+--------+--------+
| COUNT | 1XX | 2XX | 3XX | 4XX | 5XX | METHOD |                    URI                    |  MIN  |  MAX   |   SUM    |  AVG  |  P50  |  P90  |  P95   |  P99   | STDDEV |
+-------+-----+-----+-----+-----+-----+--------+-------------------------------------------+-------+--------+----------+-------+-------+-------+--------+--------+--------+
| 689   | 0   | 644 | 0   | 45  | 0   | GET    | /api/player/competition/[\w-]+/ranking    | 0.008 | 16.556 | 1138.462 | 1.652 | 0.020 | 8.040 | 9.648  | 16.516 | 3.571  |
| 433   | 0   | 394 | 0   | 39  | 0   | GET    | /api/player/player/[\w-]+                 | 0.004 | 16.472 | 704.476  | 1.627 | 0.068 | 6.948 | 9.792  | 15.804 | 3.419  |
| 86    | 0   | 77  | 0   | 9   | 0   | POST   | /api/organizer/competition/[\w-]+         | 0.004 | 16.556 | 159.523  | 1.855 | 0.460 | 5.688 | 8.105  | 16.556 | 2.930  |
| 16    | 0   | 14  | 0   | 2   | 0   | GET    | /api/admin/tenants/billing                | 0.052 | 17.668 | 104.673  | 6.542 | 5.968 | 9.484 | 17.668 | 17.668 | 3.575  |
| 7     | 0   | 7   | 0   | 0   | 0   | POST   | /api/organizer/players/add                | 2.024 | 9.292  | 31.120   | 4.446 | 3.656 | 9.292 | 9.292  | 9.292  | 2.575  |
| 1     | 0   | 1   | 0   | 0   | 0   | POST   | /initialize                               | 9.336 | 9.336  | 9.336    | 9.336 | 9.336 | 9.336 | 9.336  | 9.336  | 0.000  |
| 19    | 0   | 19  | 0   | 0   | 0   | GET    | /api/organizer/billing                    | 0.004 | 4.529  | 8.533    | 0.449 | 0.012 | 3.052 | 4.529  | 4.529  | 1.181  |
| 37    | 0   | 36  | 0   | 1   | 0   | POST   | /api/organizer/competitions/add           | 0.004 | 0.260  | 1.496    | 0.040 | 0.028 | 0.076 | 0.172  | 0.260  | 0.046  |
| 97    | 0   | 87  | 0   | 10  | 0   | GET    | /api/player/competitions                  | 0.000 | 0.048  | 0.984    | 0.010 | 0.004 | 0.024 | 0.036  | 0.048  | 0.012  |
| 11    | 0   | 6   | 0   | 5   | 0   | POST   | /api/admin/tenants/add                    | 0.008 | 0.096  | 0.432    | 0.039 | 0.020 | 0.088 | 0.096  | 0.096  | 0.038  |
| 13    | 0   | 12  | 0   | 1   | 0   | POST   | /api/organizer/player/[\w-]+/disqualified | 0.004 | 0.020  | 0.128    | 0.010 | 0.008 | 0.012 | 0.020  | 0.020  | 0.004  |
| 23    | 0   | 23  | 0   | 0   | 0   | GET    | /api/organizer/players                    | 0.004 | 0.016  | 0.124    | 0.005 | 0.004 | 0.012 | 0.012  | 0.016  | 0.004  |
| 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  | 0.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  | 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  | 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  | 0.000  |
+-------+-----+-----+-----+-----+-----+--------+-------------------------------------------+-------+--------+----------+-------+-------+-------+--------+--------+--------+
kazuphkazuph

alpのdiffを実行する

function alpdiff () {
 
 PATTERN="/api/player/competition/[\w-]+/ranking,/api/player/player/[\w-]+,/api/organizer/competition/[\w-]+,/api/organizer/player/[\w-]+/disqualified,/image/\d+"
  echo diff $1 $2
  cat $1 | sudo alp json --sort sum -r -m  $PATTERN --percentiles="50,90,95,99" --dump dumpfile1.yaml
  cat $2 | sudo alp json --sort sum -r -m $PATTERN --percentiles="50,90,95,99" --dump dumpfile2.yaml
  alp diff -r --percentiles="50,90,95,99" --show-footers -o count,1xx,2xx,3xx,4xx,5xx,method,uri,min,max,sum,avg,p50,p90,p95,p99,stddev dumpfile1.yaml dumpfile2.yaml
}

alpdiff /tmp/access_2022081215{22,59}.log
kazuphkazuph

GoでRemoteでデバッグする

ssh isucon でログインできる状態で vscodeのremote sshで接続する。
その状態で以下のファイルを生成。"program""cwd"は適宜合わせる。

.vscode/launch.json
{
  // IntelliSense を使用して利用可能な属性を学べます。
  // 既存の属性の説明をホバーして表示します。
  // 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Launch Package",
      "type": "go",
      "request": "launch",
      "mode": "auto",
      "program": "${workspaceFolder}/webapp/go/cmd/isuports",
      "cwd": "${workspaceFolder}/webapp/go",
    }
  ]
}

systemdですでにgoのサーバーが起動しているので、stopしている状態で、vscodeで「F5」を押すとデバッグ可能な状態で起動する。あとはブレークポイントをvscode上で設定してbenchを回すとその箇所をデバッグできる。

sudo systemctl stop isuports.service

VSCode内で複数のGoプロジェクトを扱う場合 or go.modがVSCodeのワークディレクトリのカレントにない場合

settings.json
    "go.toolsEnvVars": {
        "GO111MODULE": "on"
    },
    "gopls": {
        "experimentalWorkspaceModule": true
    },
kazuphkazuph

pt-query-digestの導入

sudo apt install -y percona-toolkit

kazeburo/query-digesterの導入

git clone https://github.com/kazeburo/query-digester
sudo mv query-digester/query-digester /usr/local/bin/
sudo chmod a+x /usr/local/bin/query-digester

集計

-- 以降にmysqlへの接続情報を書く。

sudo query-digester -duration 80 -- -uroot -proot
kazuphkazuph

[実験]slowqlの導入

今のところ主役にならなそうではあるが、気になったので使ってみる。

.bashrc
+ export PATH=$HOME/go/bin:$PATH
go install github.com/devops-works/slowql/cmd/slowql-replayer@latest
go install github.com/devops-works/slowql/cmd/slowql-digest@latest

digestの実行

約10倍高速だが、個人的にqt-query-digestの方が見やすい。

$ slowql-digest -top 20 -sort-by query_time -k mysql -f /tmp/slow_query_20220714235859.log
...
Query #14
Calls                  : 5268
Hash                   : 2ed3d496c9377606d9ed8b5c423ca9ee
Fingerprint            : replace into id_generator (stub) values ('a');
Schema                 :
Min/Max/Mean time      : 946µs/45.691ms/5.495ms
p50/p95                : 4.458ms/12.291ms
Concurrency            : 83.5191%
Standard deviation     : 3.858ms
Cum Query Time         : 28.948143s
Cum Lock Time          : 11.241749s
Cum Bytes sent         : 0
Cum Rows Examined/Sent : 0/0
Cum Killed             : 0

Query #15
Calls                  : 785
Hash                   : 5ae8aba57361bd167198c8bf075b8b5c
Fingerprint            : select player_id, min(created_at) as min_created_at from visit_history where tenant_id = ? and competition_id = ? group by player_id;
Schema                 :
Min/Max/Mean time      : 135µs/312.108ms/59.347ms
p50/p95                : 46.483ms/142.011ms
Concurrency            : 134.4117%
Standard deviation     : 44.86ms
Cum Query Time         : 46.58779s
Cum Lock Time          : 1.644ms
Cum Bytes sent         : 0
Cum Rows Examined/Sent : 17160293/77470
Cum Killed             : 0

replayer実行

slow queryに含まれるクエリを再実行してくれる。
が、クエリの内容によってはロックされて最後まで実行されなかった。

slowql-replayer -u root -p -h localhost -k mysql -f /tmp/slow_query_20220715000523.log -db isuports -no-dry-run

SQL error: Error 1213: Deadlock found when trying to get lock; try restarting transaction
SQL error: Error 1213: Deadlock found when trying to get lock; try restarting transaction
SQL error: Error 1213: Deadlock found when trying to get lock; try restarting transaction

ISUCON12予選問題の場合は、init.shを実行後であれば問題なく流れるのが期待値?
selectやinsertだけなどfilterして実行ができると良いのかもしれない。

kazuphkazuph

sshrcの導入

自分の場合は .gitconfig を設定することが多いので、一旦それだけ自動化した。

sshrcのインストール(ホストのみで実行)

wget https://raw.githubusercontent.com/cdown/sshrc/master/sshrc &&
chmod +x sshrc &&
sudo mv sshrc /usr/local/bin

.gitconfig用の設定

mkdir -p ~/.sshrc.d
cp .gitconfig ~/.sshrc.d
.sshrc
if [ $(hostname | grep isucon | wc -l) == 1 ]; then
  echo 'sshrc: set config'
  cp -r $SSHHOME/.sshrc.d/.gitconfig $HOME/.gitconfig
fi

sshrcの実行

ssh isucon12q でログインできるのであれば

sshrc isucon12q

とすることで、 .gitconfig の設置が完了する。