---
# System prepended metadata

title: ISUCON3予選
tags: [過去問]

---

# ISUCON3予選

###### tags: `過去問`

# セットアップ
- EC2でインスタンス作成→マシンイメージ選択でコミュニティAMIから「ami-b34ad3b2」で検索
- セキュリティグループの設定で「すべてのTCP」、ソースに「0.0.0.0/0」を指定
- SSDは使用しない
- キーペア「isucon-sshkey」とかで作成して~/.sshに保存
- インスタンス一覧ページからアクション->接続でSSHクライアント
    - `ssh -i ~/.ssh/isucon-sshkey.pem ec2-user@ec2-<ip>.ap-northeast-1.compute.amazonaws.com`
    - `sudo su isucon`
    - `cd /home/isucon/`
- http://<ip>にブラウザからアクセスしてWebアプリにアクセスできる

※指定されているm3.xlarge、一日あたり1000円近くかかるのでつけっぱなしにしないように注意

# マニュアル (README.md)
### ベンチマークの設定と実行

1. 起動したインスタンスの TCP 80 で Web アプリケーションが起動しています
  * 初期設定では Perl 実装が起動している状態です
  * PHP実装のみ他言語と起動方法が異なるため、`/home/isucon/webapp/php/README.md` を参照してください
  * SignInのためのテスト用アカウントは username: isucon1 password: isucon1 を使用してください
2. ベンチマークをテスト実行するためには、下記コマンドを入力してください
  
  ```
  $ sudo isucon3 test [--workload N]
  ```
  
  テスト実行の場合は、初期設定時に投入されているデータベースのデータはリセットされません
3. 本番計測を行う場合は、以下のコマンドを入力してください
  
  ```
  $ sudo isucon3 benchmark [--init /path/to/script] [--workload N]
  ```
  
  * 初期設定時と同様の状態にデータベースがリセットされます。そのため、実行開始までに数十秒程度の時間がかかります
    * データベースリセットのためには以下の条件を満たす必要があります
      * MySQL が起動している
      * user `isucon`, password なしで `isucon` データベースに接続可能
    * MySQL の `root` ユーザのパスワードは `root` です
  * `--init` 引数に任意の実行可能なコマンド(スクリプトなど)を指定することで、データベースリセット後に任意の処理を行うことができます
    * MySQL 以外のストレージにデータを移すなどの処理はここで行ってください
    * ただしコマンドの実行終了を待つのは実時間で60秒までで、それ以上経過すると強制終了されます
  * `--workload` は省略可能で、デフォルト 1 です
    * 2,3,4...と数値を増やすごとにベンチマークで掛けられる負荷が上がります
    * スコアの集計は `workload` の値によらず、表示されたものが最終結果となります

### HOW TO RUN (go/README.md)

    $ go get github.com/go-sql-driver/mysql
    $ go get github.com/gorilla/mux
    $ go get github.com/gorilla/sessions
    $ go get github.com/bradfitz/gomemcache/memcache
    $ go build -o app
    $ ./app

# やったこと (@ryunosuke)
6/20 17:50
- GO実装に切り替え
    - `source ~/env.sh`
    - `ps -aux | grep starman`でプロセス見つけてkill
    - README.mdの内容実行 (gorilla/sessionsのインストールでエラーになったがなんか動いた)
    - `./app &`

- とりあえずベンチを動かす
    - `sudo isucon3 benchmark`

初期点いい場合で2000点近く
GET memo/<id>で404エラー
POST signinで500エラー (timeout)
memoで400エラー
減点がめっちゃでかい
なんかベンチ実行するごとにエラー増える -> 何かしらのリソースが残ったままになってる？

2021/06/20 18:37:45 [FAIL] //h2/text() match Hello\s+isucon63\!
2021/06/20 18:37:45 [FAIL] element is not found: //input[@value='SignOut' and @type='submit']

6/20 18:30
- git管理
    - `cd ~/.ssh && ssh-keygen`
    - `cat id_rsa.pub`
    - レポジトリ作成 && ssh URLコピー
    - git remote add origin git@github.com:isucon-kuolc38th/isucon3-qualify.git
    - git config --global user.name "server"
    - git add .
    - git commit -m "initial commit"
    - git push origin master

6/20 18:50
- アプリ触る
    - メモの一覧が表示されてる
        - 直近の100件、publicのトータル
        - 最初の1行、ユーザー名、投稿時刻
        - タイトルと本文の区別はない
    - メモの詳細にいくとmarkdown的なものが表示されてそう 
        - 投稿者、投稿時刻、本文
        - リンクに飛べる (githubでリンク切れ -> 本番時は表示されていた？)
        - 前のやつと次のやつ (publicごと、privateごと)
    - ユーザー名とパスワードでログイン
    - ログインするとMyPage (自分が作ったメモ)
        - privateとpublicがある
        - privateはマイページのみ、publicはトップに表示される
    - memoをpostできる
        - 空行やスペースがトリムされてるっぽい

ベンチ後: total memo 20000

6/20 19:05
- rootが必要な操作が多いので`sudo su`でルートになっておくと楽かも
- `sudo yum update`
- Apacheをnginxに入れ替え
    - `sudo apachectl stop`
    - `sudo yum install nginx`
    - `cd ~/webapp && mkdir -p nginx/config && cd nginx/config && sudo cp /etc/nginx/nginx.conf`
    - `cd /etc/nginx && sudo ln -s ~/webapp/nginx/config/nginx.conf`
    - gitでpush & ローカルで適当に修正
    - `sudo service nginx start`
- MySQL設定追加
    - ローカルでmysql/my.cnf作成 & push
    - `cd /etc && sudo mkdir mysql && cd mysql && sudo ln -s ~/webapp/mysql/my.cnf`
    - `sudo service mysql restart`
        - mysql_safeのPIDを指定しないと`ERROR! The server quit without updating PID file conf`というエラーが出てmysqlが起動しないので注意
        - logファイルがない場合、作成して権限設定`sudo mkdir /var/log/mysql && sudo chown -R mysql:mysql /var/log/mysql`
- alpインストール
    - wget https://github.com/tkuchiki/alp/releases/download/v1.0.3/alp_linux_amd64.zip
    - unzip alp_linux_amd64.zip
    - sudo mv alp /usr/local/bin/
    - rm alp_linux_amd64.zip 
- benchmarkツール作成
    - log初期化
    - ベンチマーク実行
    - アクセスログ表示

6/20 21:30

6/20 23:10
ローカル環境のセットアップ
- docker-compose
- SQLセットアップスクリプトの配置
- フォルダの作成
- go.mod作成

6/20 24:30

6/21 19:30
ローカル環境のセットアップ続き
- nginxのテンプレートを使って環境ごとのdefault.confを作成
    - conf.dが元からないと生成されないので注意
- フォルダ整理（他の言語削除）
- 環境変数整理
    - 今回の場合config.jsonが用意されていたのでそれも使ってる
    - env.shで管理？何かしら決めておく
    
    

6/22 21:15
- GOPATH外のgo build
    - /home/isucon/local/go/bin/src/{package}にsessionsのエイリアスをおく
    - nginxのバージョンが古くtemplateが利用できないのでdefault.conf.prodを作ってエイリアスをおく
    - nginxのlogフォルダのエイリアス
        - sudo ln -s /home/isucon/webapp/nginx/log/ ./nginx
        - chown -R nginx:nginx nginx/
    - mysqlのlogフォルダのエイリアス
        - sudo ln -s /home/isucon/webapp/mysql/log/ ./mysql
        - chown -R mysql:mysql mysql/
    - mysql設定修正
        - sudo ln -s /home/isucon/webapp/mysql/config/my.cnf 

mysqlがぶっ壊れたので最初からやり直し
```
# レポジトリインストール
rm -rf webapp
git clone git@github.com:isucon-kuolc38th/isucon3-qualify.git
mv isucon3-qualify webapp
sudo su

# mysqlエイリアス
service mysql stop
cd /etc && ln -s /home/isucon/webapp/mysql/config ./mysql
chown -R mysql:mysql /home/isucon/webapp/mysql
cd /var/log/ && ln -s /home/isucon/webapp/mysql/log ./mysql
service mysql start

# nginxエイリアス
cd /etc && cp nginx/* /home/isucon/webapp/nginx/config/
rm -rf nginx/
ln -s /home/isucon/webapp/nginx/config ./nginx
cd /var/log && rm -rf nginx
ln -s /home/isucon/webapp/nginx/log ./nginx
chown -R nginx:nginx /home/isucon/webapp/nginx

# Apacheを落としてnginxを立ち上げる
service httpd stop
service nginx start

# goのライブラリのインポート設定
cp -r /home/isucon/sessions/ /home/isucon/local/go/bin/src/isucon3-qualify/

# 起動時にnginx/goが立ち上がるように設定
chkconfig httpd off
chkconfig nginx on

chmod 777 /home/isucon/webapp/env.production.sh 
vim /etc/supervisord.conf 
# /home/isucon/env.sh app となってる箇所を
# /home/isucon/webapp/env.production.sh app に変更
# [isucon_go]のautostartをtrueに、[isucon_ruby]のautostartをfalseにする
supervisorctl restart
```

6/28 21:55
memoHandlerで404がめっちゃ出てるので調べる
- olderとnewerの計算がめっちゃ遅い
- マイページの時は公開非公開合わせて全部
- それ以外の時は公開のもののみ、ユーザーは一緒

先にmemoCreateが落ちてる方が原因っぽいのでそっち

logファイルの権限適切に設定してないとlogが書き込めなくなる
sudo truncateも良くないかも

initエンドポイント 

- memoTotalCountのキャッシュ
- created_at -> id

FAILが減ってスコアが出るようになったがRAW_SCOREは下がった

6/29 23:00
- memos.userのindexを張る
RAW_SCOREは上がったがFAILが増えてスコアが出なくなった

エラー系はログインで失敗してるのが原因っぽい
ログ見るとmemcacheがタイムアウトしてsigninが500を出している
調べるとmemcacheのオプションをいじると良いらしい
ライブラリのバージョン的にコンフィグができなかったのでlocalのgomemcache/memcacheのコードを直接いじる

```
const DefaultTimeout = *time.Second*

const (
        buffered            = 8 // arbitrary buffered channel size, for readability
        maxIdleConnsPerAddr = *10* // TODO(bradfitz): make this configurable?
)
```

↑ダメだったので自前でInMemoryStoreを組み直す

go最新版インストール
wget https://golang.org/dl/go1.16.5.linux-amd64.tar.gz

FAIL直る
現在3000点近く

6/30 22:10
usernameをmemoに入れる

user: 400
memo: 40000

user数は知れてる・更新されうるフィールドもlastAccessしかないのでuserごとキャッシュ

top/recentのuserをキャッシュから取得 -> 3400
getUserをキャッシュで -> 3500

recentが依然遅い
OFFSETがやばそうなのでIDListをキャッシュで持ってアンカーを計算
-> 7200 (!!!)

alp見ておそそうなやつから改善
スロークエリも使ったけどミスリードになる可能性もあると思った（そもそもクエリが不要になる場合もある・インデックス貼ってもあんまり変わらない場合も）

ほしいと思ったツール
benchを回して結果を自動でコミット
CPUとかメモリはほぼ見てない
アプリのリスタート
pprofのログ管理

7/1 20:30
newer/olderをキャッシュ
思ったより上がらず、他にボトルネックがある？

-> 7400

pprof

```
go get github.com/google/pprof/
```

mainに追加
```
// gorilla/muxの場合
r.PathPrefix("/debug/pprof/").Handler(http.DefaultServeMux)
```


```
export PPROF_TMPDIR=$HOME/webapp/pprof
go tool pprof http://localhost/debug/pprof/profile

go tool pprof -http=":8080" pprof/pprof.app.samples.cpu.001.pb.gz 
```

pprofみてfirst_lineをindexに置き換えて改善

