あいつの日誌β

働きながら旅しています。

ISUCON9 予選を復習する時の手順

あらすじ

復習するためにインスタンスを作成した時のメモを残します

多少は環境構築が楽になると思うので、イメージは予選の時に使用した isucon9-qualify-v1 を使用します。ただしこのイメージにはベンチマーカーが含まれていません。

この記事では以下の作業を行って webapp と benchmarker を1台のインスタンスに同梱させる方法を記します。

  • ECS の準備
  • make に必要な package を取得
  • isucon9q のソースコードを取得
  • 初期データを用意
  • ベンチマーカーを make

ECS を準備

基本的には以下の作業を行えばOK

https://gist.github.com/sota1235/a7356a5909af264c1c1c840dd216c98f

Elastic Compute Service(ECS)

カスタム購入

基本構成 (必須)

価格モデル: 従量課金

リージョン: アジア東北1ゾーンA

インスタンスタイプ: ecs.sn1ne.large

共有イメージ: isucon9-qualify-v1

Ultra クラウドディスク: 40GiB

ネットワーク (必須)

ネットワーク: VPC デフォルトのまま

ネットワーク課金タイプ: トラフィック課金 100MBps

セキュリティグループ: 80, 443, 22 が空いてればOK

システム構成

キーペア: 適宜

インスタンス名: 適宜

ホスト名: 適宜 isucon9q-001 のように 1-3 の連番を振ると便利

/etc/hosts を編集

以下のような感じで編集すれば良いでしょう。

47.74.xxx.xxx isucon9q-001 isucon9.catatsuy.org

https://isucon9.catatsuy.org/ で画面が表示される事を確認し、ssh でサーバーにログインできる事を確認します。

PATH を通す

ssh でサーバーにログインします。root user のまま作業を行っています。 PATH を通したいので isucon user の bash_profile をコピーします。

cat /home/isucon/.bash_profile >> ~/.bash_profile

PATH が通っている事を確認します

exec $SHELL -l
which go
/home/isucon/local/go/bin/go

make に必要な package を取得 (docker, unzip)

まず unzip をインストールします

apt update
apt install -y unzip

続いて Docker。以下の URL の Install using the repository INSTALL DOCKER ENGINE - COMMUNITY の作業を行います。

https://docs.docker.com/install/linux/docker-ce/ubuntu/

Install using the repository

apt install \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg-agent \
    software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) \
   stable"

INSTALL DOCKER ENGINE - COMMUNITY

apt update
apt install -y docker-ce docker-ce-cli containerd.io

動作確認をする

docker run hello-world

isucon9q のソースコードを取得 (git clone)

この記事では /home/isucon の直下に isucon9-qualify を配置します。

cd /home/isucon
git clone https://github.com/isucon/isucon9-qualify.git

初期データを用意

make を実行します。docker を使って初期データを作成します。

cd /home/isucon/isucon9-qualify/initial-data/
make

生成された initial.sql は、/home/isucon/isucari/webapp/sql/initial.sql とは異なるデータになっているので同期させます。

cp /home/isucon/isucon9-qualify/initial-data/result/initial.sql /home/isucon/isucari/webapp/sql/initial.sql

ベンチマーク用画像データをダウンロード

cd /home/isucon/isucon9-qualify/initial-data/
curl -L -O "https://github.com/isucon/isucon9-qualify/releases/download/v2/bench1.zip"
unzip bench1.zip
rm -rf images
mv v3_bench1 images

ベンチマーカーを make (bin/benchmarker)

cd /home/isucon/isucon9-qualify/
make

動作確認をします。最後に以下のような出力がされていれば OK です。

./bin/benchmarker
<省略>
2019/09/15 01:04:20 main.go:180: === final check ===
2019/09/15 01:04:20 main.go:212: 2110 0
{"pass":true,"score":2110,"campaign":0,"language":"Go","messages":[]}

あとそれから

今回の isucon で私がボトルネックを検出するために作った計測ツールを先日 npm publish しました。

github.com

使い方

cd /home/isucon/isucari/webapp/nodejs
npm install performance-measure

edit /home/isucon/isucari/webapp/nodejs/index.ts

diff --git a/index.ts b/index.ts
index f8a6ca6..d769f00 100755
--- a/index.ts
+++ b/index.ts
@@ -15,6 +15,9 @@ import crypt from "crypto";
 import bcrypt from "bcrypt";
 import {paymentToken, shipmentCreate, shipmentRequest, shipmentStatus} from "./api";

+const PerformanceMeasure = require('performance-measure')
+const m = new PerformanceMeasure()
+
 const execFile = util.promisify(childProcess.execFile);

 type MySQLResultRows = Array<any> & { insertId: number };
@@ -251,21 +254,42 @@ async function getDBConnection() {
     return fastify.mysql.getConnection();
 }

+// measure
+fastify.addHook('preHandler', function (request :any, reply: any, next) {
+  m.start(request.raw.id)
+  next()
+})
+
+fastify.addHook('onSend', function (request: any, reply: any, _, next) {
+  const routeId = reply.context.config.statsId ? reply.context.config.statsId : request.raw.url
+  m.endAs(request.raw.id, routeId)
+  next()
+})
+
+fastify.get('/__stats__', async (req, reply) => {
+  const style = "font-family: 'Fira Mono', 'Andale Mono', 'Consolas', 'monospace';"
+  reply
+    .header('Content-Type', 'Content-type: text/html; charset=utf-8')
+    .send(`<pre style="${style}">${m.print()}</pre>`)
+})
+
+function c (statsId: string) {
+  return { config: { statsId } }
+}
 // API
 fastify.post("/initialize", postInitialize);
-fastify.get("/new_items.json", getNewItems);
-fastify.get("/new_items/:root_category_id(^\\d+).json", getNewCategoryItems);
-fastify.get("/users/transactions.json", getTransactions);
-fastify.get("/users/:user_id(^\\d+).json", getUserItems);
-fastify.get("/items/:item_id(^\\d+).json", getItem);
+fastify.get("/new_items.json", c("/new_items.json"), getNewItems);
+fastify.get("/new_items/:root_category_id(^\\d+).json", c("/new_items/:root_category_id(^\\d+).json"), getNewCategoryItems);
+fastify.get("/users/transactions.json", c("/users/transactions.json"), getTransactions);
+fastify.get("/users/:user_id(^\\d+).json", c("/users/:user_id(^\\d+).json"), getUserItems);
+fastify.get("/items/:item_id(^\\d+).json", c("/items/:item_id(^\\d+).json"), getItem);
 fastify.post("/items/edit", postItemEdit);
 fastify.post("/buy", postBuy);
 fastify.post("/sell", postSell);
 fastify.post("/ship", postShip)
 fastify.post("/ship_done", postShipDone);
 fastify.post("/complete", postComplete);
-fastify.get("/transactions/:transaction_evidence_id(^\\d+).png", getQRCode);
+fastify.get("/transactions/:transaction_evidence_id(^\\d+).png", c("/transactions/:transaction_evidence_id(^\\d+).png"), getQRCode);
 fastify.post("/bump", postBump);
 fastify.get("/settings", getSettings);
 fastify.post("/login", postLogin);
@@ -277,10 +301,10 @@ fastify.get("/", getIndex);
 fastify.get("/login", getIndex);
 fastify.get("/register", getIndex);
 fastify.get("/timeline", getIndex);
-fastify.get("/categories/:category_id/items", getIndex);
+fastify.get("/categories/:category_id/items", c("/categories/:category_id/items"), getIndex);
 fastify.get("/sell", getIndex);
-fastify.get("/items/:item_id/edit", getIndex);
-fastify.get("/items/:item_id/buy", getIndex);
+fastify.get("/items/:item_id/edit", c("/items/:item_id/edit"), getIndex);
+fastify.get("/items/:item_id/buy", c("/items/:item_id/buy"), getIndex);
 fastify.get("/buy/complete", getIndex);

 async function getIndex(_req: any, reply: FastifyReply<ServerResponse>) {
@@ -309,7 +333,7 @@ async function postInitialize(req: FastifyRequest, reply: FastifyReply<ServerRes

     const res = {
         // キャンペーン実施時には還元率の設定を返す。詳しくはマニュアルを参照のこと。
-        campaign: 0,
+        campaign: 2,
         // 実装言語を返す
         language: "nodejs",
     };

node.js を起動

systemctl stop    isucari.golang.service
systemctl disable isucari.golang.service
systemctl start   isucari.nodejs.service
systemctl enable  isucari.nodejs.service

ベンチ実行

2019/09/15 02:16:57 main.go:180: === final check ===
2019/09/15 02:16:58 main.go:212: 5410 0

GET https://isucon9.catatsuy.org/__stats__

name                                              size  sum         max      min      mean
------------------------------------------------  ----  ----------  -------  -------  -------
/buy                                               529  1379186.94  3843.30     0.03  2607.16
/items/:item_id(^\d+).json                        1066   802732.08  3839.38     1.30   753.03
/login                                             704   782362.84  4105.26   225.83  1111.31
/settings                                          666   545591.99  3765.35     4.80   819.21
/new_items/:root_category_id(^\d+).json            366   383428.45  4208.59   162.25  1047.62
/new_items.json                                    126   145147.42  4250.52   217.48  1151.96
/users/transactions.json                            50   111228.35  7035.00    45.94  2224.57
/users/:user_id(^\d+).json                         119    97799.55  3978.04    47.27   821.84
/sell                                               40    24101.81  1527.11     0.03   602.55
/ship                                               10     8616.09  2033.46     0.38   861.61
/complete                                            8     8140.25  2017.89     9.76  1017.53
/ship_done                                          11     7456.80  1591.32     0.03   677.89
/initialize                                          1     6554.08  6554.08  6554.08  6554.08
/bump                                                7     3852.41  1223.84    15.66   550.34
/transactions/:transaction_evidence_id(^\d+).png    10     3465.90  1067.22     0.85   346.59
/items/edit                                          4     1496.02  1064.49     3.50   374.01
/reports.json                                        1      477.86   477.86   477.86   477.86
/static/js/2.ff6e1067.chunk.js                       1      244.51   244.51   244.51   244.51
/upload/30f7679c97b5bb567dc1b38e3f52601a.jpg         2       22.00    21.72     0.27    11.00
/upload/0674e4389f096c8afd8b114b89275de6.jpg         1       21.18    21.18    21.18    21.18
/upload/5e8f91b52cb52c6b8d72533cab6f63b7.jpg         1       19.60    19.60    19.60    19.60
/upload/0d14fcbed9720312fafb8573d8556f51.jpg         1       13.33    13.33    13.33    13.33
/upload/1a571465c3b144d02490e3115208f8ca.jpg         1       11.76    11.76    11.76    11.76
/upload/30325ba77825014e96d42b19c18b9ee4.jpg         1        7.67     7.67     7.67     7.67
/static/js/runtime~main.a8a9905a.js                  1        7.46     7.46     7.46     7.46
/upload/9b2bb8fb771e26dfbf335798f2adbd7a.jpg         1        3.50     3.50     3.50     3.50
/upload/62da8c459ff224e10f297a8deb207349.jpg         1        2.53     2.53     2.53     2.53
/upload/17aba6bc9657f59ba91ec376d08c3255.jpg         1        2.52     2.52     2.52     2.52
/upload/2dd7e13de6eadfc195a94c9c4d34884c.jpg         1        2.51     2.51     2.51     2.51
/upload/393afbdea5b3308247015ad89db679d9.jpg         1        1.97     1.97     1.97     1.97
/upload/26e8c534dca0c993c4fd0581c8a75a20.jpg         1        1.95     1.95     1.95     1.95
/upload/2efc92a5d55e12337d8f50d80ee0cf0b.jpg         1        1.95     1.95     1.95     1.95
/upload/2e0ba97d1012991aae5f83a447105342.jpg         1        1.95     1.95     1.95     1.95
/upload/5f57c8f5f424d3187a5097e5bc349cbc.jpg         1        1.86     1.86     1.86     1.86
/upload/30f113dd78473b35e8b3df60bc1dc7bd.jpg         1        1.86     1.86     1.86     1.86
/upload/7c6b3916fe91112d88d9f810b745aae5.jpg         1        1.72     1.72     1.72     1.72
/upload/169534d0a6d9d37be61d4737bfcd0a81.jpg         1        1.54     1.54     1.54     1.54
/upload/30eacdcf0bd8b425ce8112845974d8be.jpg         1        1.31     1.31     1.31     1.31
/upload/3a47c2abbaa28eeb79b322033b56c175.jpg         1        1.18     1.18     1.18     1.18
/upload/2f233413a51dfd3983f0e9feba54bcb5.jpg         1        1.15     1.15     1.15     1.15
/upload/3104fe69b6596368bb86db005c17b3f1.jpg         1        1.11     1.11     1.11     1.11
/upload/2e88083aaaa21439d3cb8520ad38d8fd.jpg         1        1.11     1.11     1.11     1.11
/upload/40c71b0281e1cbff0b8e2c71e355b6e0.jpg         1        1.08     1.08     1.08     1.08
/upload/30935566cc5c445de0920cdb12950a17.jpg         1        1.02     1.02     1.02     1.02
/upload/3fd4a90b3c3f80755468aac1292d1d3f.jpg         1        1.00     1.00     1.00     1.00
/upload/2ccfaf5b7ace96f26e9ba26a7002c4ad.jpg         1        0.90     0.90     0.90     0.90
/static/js/main.babc3d4d.chunk.js                    1        0.88     0.88     0.88     0.88
/upload/47f8232fc19e4a8034b8580eb6a69ada.jpg         1        0.82     0.82     0.82     0.82
/upload/17892821abdb57d7e8cf05e750f1b063.jpg         1        0.79     0.79     0.79     0.79
/upload/3118b492c1b976decd88bf3c71cd7813.jpg         1        0.77     0.77     0.77     0.77
/static/css/main.19393e92.chunk.css                  1        0.69     0.69     0.69     0.69
/upload/30e884e88d49d8c6979f30cf89c5954e.jpg         1        0.63     0.63     0.63     0.63
/upload/30cb5e2438a8d2d31b5cc388a2ec665b.jpg         1        0.62     0.62     0.62     0.62
/upload/270e6b5b8bd15f200f54d617449a8150.jpg         1        0.60     0.60     0.60     0.60
/upload/15fb39a6cd94ce77f8903879aecc7945.jpg         1        0.59     0.59     0.59     0.59
/upload/2e7c0c29569c8bfe80c9b46367db87b2.jpg         1        0.56     0.56     0.56     0.56
/upload/03024b6a442d9932fc55919b1a6dbcab.jpg         1        0.54     0.54     0.54     0.54
/upload/30d141435d573e446a935b8ebe4c0270.jpg         1        0.47     0.47     0.47     0.47
/upload/30e64ab77ec09bb3b083d22dae89a203.jpg         1        0.46     0.46     0.46     0.46
/upload/30333707b92970cad1817c46c0726049.jpg         1        0.44     0.44     0.44     0.44
/upload/237058d87bb27255efcda80870d5af5b.jpg         1        0.41     0.41     0.41     0.41
/upload/30480d069d071b8a7beff3a6529b7a5b.jpg         1        0.40     0.40     0.40     0.40
/upload/534c5fffb5cd56f903caa807ea8af714.jpg         1        0.38     0.38     0.38     0.38
/upload/3d9e29dc8f9a8af8913e83e317f2cfd1.jpg         1        0.36     0.36     0.36     0.36
/upload/301712d7770ac99623d90a88cc2c663c.jpg         1        0.35     0.35     0.35     0.35
/upload/41ddf24e267a66474d01afaff368f046.jpg         1        0.35     0.35     0.35     0.35
/upload/30471846202de60d114ee201c3ef3166.jpg         1        0.34     0.34     0.34     0.34
/upload/2f8454793d91d4a17a8932474d50dd92.jpg         1        0.34     0.34     0.34     0.34
/upload/0e480baa07ce38a1a10a34909460e5f6.jpg         1        0.33     0.33     0.33     0.33
/upload/302815cd68966575618cb9ccc4afafc6.jpg         1        0.33     0.33     0.33     0.33
/upload/30c480dfca6870d6a6b710189e162861.jpg         1        0.31     0.31     0.31     0.31
/upload/30c6ae135ad2a68517f2baf58bb9d7fa.jpg         1        0.29     0.29     0.29     0.29

こちらからは以上です。

追記

運営側へのお礼を言い忘れてました。今年も楽しく参加させて頂きました。毎年こんなに効率よくエンジニアとしての力量が向上するイベントを開催して頂いて感謝しております。次回も開催して欲しいといつも思っています。これだけの規模のイベントをハンドリングできる強いチームは他に存在していないと思うので継続してもらえるとありがたいです。今年も運営のみなさまお疲れさまでした。