私が歌川です

@utgwkk が書いている

最近読んだ百合漫画

積んでる百合漫画が多いし可処分時間は有限。Kindle Paperwhiteを買ったのでもうちょっと読むペースが上がるといいですね*1

酒と鬼は二合まで

お酒を飲まないと死ぬ上に、人間に与えられたお酒しか飲めない身体というのはなかなかに不便そう。ぐるぐる回りつづけた虎はそのうちバターになる。コインランドリーの待ち時間って退屈ですよね。続きが気になるところで1巻は終了。

読みながら、そういえば最近カクテルを全然飲んでないなーということを思い出していた。以前は友だちにカクテルを作ってもらって飲むことがあったのだけれど、このご時世だと難しそう。

第1話が無料で読めるので読んでください。

magazine.jp.square-enix.com

あなたと私の周波数

一人で叫ぶのと、誰かが聞いているかもしれないところに向かって叫ぶのとでは意味が変わってきそう。誰かにフォローされている限定公開アカウントで叫ぶみたいなイメージ。

同人女百合アンソロジー

ステレオタイプな同人オタクっぽい気もするけどけっこう思い当たる節があって、どういう話題が地雷なのかは分からないものですな、と思いつつ休憩しながら読み進めた。

そろそろまたコミケに行きたい気がする。

*1:これについては別の記事で書く

ISUCON11予選落ち #isucon

チーム :innocent::innocent::innocent: *1 で出場しました。メンバーは去年と同じく id:nonyleneid:wass80 です。最終スコアは13369点でした。最後のベンチマークでは12000点ぐらいを記録しました。

isucon.net

やったこと

今年もGo言語で出場しました。他のメンバーがやったことはそのうちブログ記事が出るはずなので、あとで参照します。細かい実装などはリポジトリを参照してください。

github.com

splunk導入

splunkでトレーシングを行えるようにするために ctx を引き回したり、トレーシング用のライブラリを導入したりしました。練習していたので去年ほどは手こずらなかったです。

pprof導入

echoにpprofを導入しました。echo v4用にフォークされたechopprofを探して使えたので、これも去年ほど時間はかかりませんでした。

アイコン画像の静的ファイル化+etagによるキャッシュ

アイコン画像をDBから読み出しているのを見かけたので、ファイルに書き出して参照するようにしました。ログインユーザーのISUじゃない場合は404を返す実装になっていたので、nginxから返すようにするのは断念しました。nginxの X-Accel-Redirect ヘッダは聞いたことはあったけどこういう場合に使えるのか、と他チームの参加記を読んで感心しました。

そのあとetagを返して If-None-Match ヘッダを見て304を返す実装を入れました。

インデックスを貼る (失敗)

いつものようにアプリケーションから発行されるクエリを眺めて、コンソールで CREATE INDEX して回りました。が、初期化時にテーブルを丸ごと作り直していることに競技終了まで気づいていませんでした。これは私が悪いので石を投げてください。

isu_condition (jia_isu_uuid, `timestamp`) とかに効かせたらMySQLだけでも多少マシになったはずなのでめちゃくちゃ悔しい……。そしてチームのScrapboxを見返したら全く同じインデックスを2回貼ってるのに気づきました。これは私が悪いので石を投げてください (再掲)。

isu_condition テーブルをInfluxDBに載せ替える (断念)

isu_condition テーブルは明らかな時系列データなのでInfluxDBに載せ替えよう、という提案にゴーを出して、高速にInfluxDBに入門しました。InfluxQLを使うとほとんどSQLみたいな書き味で、複雑なことをしないクエリはすぐに移植できました。

しかし後述するようなさまざまな苦難が襲いかかり、最終的には載せ替えるのを断念して、ブランチを切り替えました。

データ不整合に苦しめられる

ISUのコンディションをDBから取得する実装をInfluxDBに移植してもらったところで、データ不整合でベンチマークがfailしまくるようになりました。登録されるはずのISUが登録されていないのではないか、InfluxDBにbatch insertする箇所が呼ばれていないけどなぜなのか、といろいろな箇所にログ出力を差し込んでいましたがなかなか原因は分かりませんでした。

原因が分かったのはなんと2時間後で、初期化時にInfluxDBに isu_condition テーブルの初期データをinsertするようにしたら直りました。有史以来データ不整合がずっと起こっていたというわけです。これに気づくのにこんなに時間がかかったのはマジで悔しい……。

/api/trend のInfluxDB化がうまくいかず

ついに isu_condition を参照している箇所を全てInfluxDB化できましたが、ベンチマークを走らせるとまたもやデータ不整合でfailしました。なぜかtimestampが0になっているので明示的にInfluxDBに記録する、アプリケーションでソートする、などを高速にやりましたがデータ不整合を直せず、実装を差し戻すことになりました。

POST /api/condition/:jia_isu_uuid でbulk insertする

競技終了ギリギリにInfluxDBに載せ替えるのを断念して、MySQLを使う実装に戻して最後にできることをやろう、ということでbulk insertを実装しました。commit logを見たら18:41*2に実装完了してて、しかも一発でpassしててすごすぎる。

placeholderを文字列結合で増やしていましたが、こうしなくてもsqlxの NamedExec を使うとbulk insertできるというのをあとで教えてもらいました。

github.com

反省

必殺技に全てを振ってしまった

8時間の大半を必殺技に振って、最終的に差し戻したのが何よりも悔やまれます。たしかにInfluxDBの得意領域であろうとは思いましたが、知見がなく見通しのつきにくい道に全てを振ってしまったな、と思い返しています。ある程度のデータの遅延が許される箇所はアプリケーションやRedisなどでキャッシュするとか、もうちょっと負荷を緩和する策は取れたはずです。

とはいえとはいえ、カチッとハマったらかなりMySQLの負荷を下げられて点数を伸ばせたのではないかと思っており、もっと時間・腕力・知見・などなどがあればな~~という気持ちもあります。

インデックス

私に石を投げてください。初期化リクエストで何が行われているかをちゃんと読んだらこんなことにはならなかったはずなので、次回のトライは「POST /initialize の処理を熟読する」です。

プロファイラを活用できなかった

splunkやpprofは最初に眺めたきり、MySQLのスローログは取ってもらったけどInfluxDB化に振りたかったので流し読みしてしまい、alpやkataribeでのアクセスログの解析はやっていない、ということでプロファイラを全く活用できていませんでした。

感想

予選でここまで洗練されたクオリティの問題が出てくるのは本当にすごいです。出場するたびにどんどん洗練されていく……。ポータルやベンチマークにもほとんど不自由なく競技に集中できました。

CloudFormationでインスタンスを構築するのも便利でよかったです。手でポチポチ設定して3台立てる、だと操作ミスでうまく動かないこともあると思うけど、YAMLをアップロードしたら待つだけでよくて快適でした。

あと、問題紹介の動画がよかったです。

今回は自分のミス (判断でも実装でも) がかなり目立ってしまったので、次回のトライに「深呼吸する」を追加しようと思います。

*1:triple innocentと読むことが多かった

*2:競技終了4分前

日記

夜中に何度か目覚めたけど布団で目を瞑って横になり続けたら回復した。今日もまあまあ疲れた感じがするけど昨日よりはマシだと思う。

普段持ち歩かないものを持ち歩くと、どこかに置き忘れてしまうリスクが高い。

今日見た夢

野球のような競技の主審をやっていた。「ような」というのは、フェンスの上や壁に隠れたところからストライク・ボールを判定していて、普通の野球とはちょっと違ったため。

追いかけ回されていたので空を飛んで逃げた。飛んでいると、テニスの練習におすすめとか、力作のイラストが描いてあるとか、マンションの売り文句がいろいろ見えた。

空を飛ぶにしても、横に飛ぶのと上に飛ぶのとでは使う力が違ってくる。

HacobuneでCGIを動かす

www.sakura.ad.jp

ということで、CGIを動かしましょう。Monkey Bananaがいいと思います。

CGIスクリプトを用意する

ダウンロードしてunzipしてリポジトリに含めるなり、docker build時にダウンロードしてくるなり、なんでもいいと思います。permissionとshebangの設定は忘れないようにしましょう。

Dockerfileを用意する

httpdイメージをベースにちょっと書きます。 monkey ディレクトリ以下にCGIスクリプトがあるものとします。

  • CGIスクリプトを設置する
  • デフォルトの index.html を消す (index.cgi を出したいので)
  • 独自の httpd.conf を設置する
FROM httpd:2.4

COPY --chown=daemon:daemon ./monkey /usr/local/apache2/htdocs

# remove default index.html
RUN rm /usr/local/apache2/htdocs/index.html

COPY ./httpd.conf /usr/local/apache2/conf/httpd.conf

monkey/dataディレクトリを .dockerignore で無視するようにしておきましょう。

httpd.confを用意する

CGIを動かすための設定項目については Docker上でApacheコンテナを作成しCGIのコンテンツを走らせるまで - ワタナベ書店 などの記事が参考になります。それに加えて設定しておくとよいものだけ抜粋して書きます。

データを記録するディレクトリは永続化したいのでボリュームをマウントすることになりますが、単にマウントするだけだと全データが丸見えなので明示的にdenyしておきます。.htaccessをボリュームに送り込むのでもよい?

<Directory "/usr/local/apache2/htdocs/data">
    Deny from all
</Directory>

正しいリクエスト元のIPアドレスをCGIから取得できるようにするために Apache と Nginx で接続元 IP を取得する方法 | Wedding Park CREATORS Blog に沿って設定を書き足しましょう。 LoadModule remoteip_module modules/mod_remoteip.so の行のコメントアウトを外して RemoteIPHeader x-forwarded-for を足せばよいです。

管理用のパスワードを環境変数経由で渡す

Twelve-Factor Appに則って、秘匿情報をコードにベタ書きせずに環境変数経由で渡すことを考えます。

httpd.confに PassEnv ディレクティブ でCGIに渡したい環境変数を指定してやれば、いつも通り $ENV{FOO} という形で参照できます。あとはアプリケーションの設定で環境変数を渡すなりなんなりすればよいです。

初期データを用意する

ボリュームは最初は空なので、このままだとCGIがうまく動きません。空データを用意する必要があります。

手っ取り早くやる、ということで以下のようなCGIを用意して一度リクエストする作戦を取りました。リクエストした後はこのCGIを消しておきましょう。

#!/usr/bin/perl
use strict;

# 設定ファイル認識
require "./init.cgi";
my %cf = set_init();

# 空ファイルを作る
system("touch $cf{logfile}");

# TODO: 初期化が完了したらこのファイルは消す
print "Content-Type: text/plain\n\n";
print "ok";

CGIをちょっと改修する

Monkey BananaではCGI::Carpというモジュールをuseしていますが、このモジュールは非推奨になってコアモジュールから外されています。新しめのPerlだとuseできないのでコメントアウトしておきましょう。

できました

ここまで来れば、あとはコンソールからぽちぽち設定してデプロイできます。GitHubリポジトリと連携すると、pushするだけで自動でデプロイされて便利です。

monkey-banana.c1.hacobuneapp.com

Hacobune特有の話題よりも、コンテナ化された環境でCGIを動かす場合の話題のほうが多かった気がします。しかしコンテナだとだいたいなんでも動かせて便利ですね。

トマトが苦手なのはどこから来ているのか

桃と間違えてトマトを食べた

台所のテーブルに置いてあるトマトを桃だと思って食べて、汁が出てきて嫌な気持ちになった、という体験を思い出せるのだけれど、本当にそんなことがあったのか??

ケチャップはいいのか

ケチャップはトマトからできているので苦手、みたいなことを考えたことはない。

トマトジュースはいいのか

ドロッとしてるなーと思いながら飲んだことがある。嫌いな味・感触だなーとは思っていなかった。

考察

汁っぽいのがダメなのか?

デスクトップマシン用のゲームパッドとして、8年ぐらい前? 高校生の頃にブックオフで中古で買ったXbox 360のコントローラーを長いこと使っていたのだけれど、LBボタンが引っかかってゲームに支障をきたしていた。風来のシレン5でいうと、セットした飛び道具を打ちづらい状態。

というわけで一念発起して新しいゲームパッドを買った。

風来のシレン5でクリアしていないダンジョンをちょっとクリアして、インストールしてチュートリアル的なところまで済ませてそれっきりだった原神をちょっとプレイしたりした。

3Dゲームは1日2時間ぐらいまでしかできない。なぜなら3D酔いするから……。戦い方は分かってきたけど最終的には剣でゴリ押すみたいな感じでシンプルになっている。神殿を回って強風が収まるところまではプレイした。

オープンワールドゲームでガチャ要素があるのは初めてだけど今のところガチャの結果がどう響いているかは分からない。最初に引いたきりだと思う。バーバラが可愛いのでできるだけ使おうとしているけどレベルが足りない。