moshisora.memo

もくもくと。

iOSDC Japan 2017 に当日スタッフとして参加してきました

ブログを書くまでがiOSDC、ということで9/15夕方〜9/17に早稲田大学で行われたiOSDC Japan 2017(iOSとその周辺にまつわる技術カンファレンス)にスタッフとして参加したレポートです。 @moshisora990です。元々リアルなイベントが好きなので、カンファレンスの運営には少しずつ取り組んでいて、去年はYAPC::ASIA HACHIOJIに出ていたりしたのですが、今年は縁あってiOSDC Japan 2017の当日スタッフとして参加させてもらったので、スタッフ参加目線で写真を交えつつ、主に会場側の目線で書いてみようと思います。きっとトークに関してはたくさんの方がエントリー書いてくれるはず…!

スタッフMTG

iOSDC開催からひと月ほど前に、当日スタッフは初めてのMTGへ。秋葉原の会場だったのですが早速設備がすごくて圧倒されてました。内容はもちろんのこと、会場説明、スケジュール、アカウント管理、各種ドキュメント整理、おおまかな担当割り振り等、事前に準備できるものを一通り共有しました。

顔見知りが何名かいらっしゃったというのもありますが、皆さん気さくな方ばかりなので特に不安事もなくこの日は終了。

準備日・前夜祭

準備日は会場設営とノベルティ等配布物の準備、前夜祭の受付をしていました。ノベルティを準備して、垂幕が広げられるとカンファレンスだなーという気持ちが一気に湧いてくるものです。

f:id:moshisora:20170915133919j:plain

f:id:moshisora:20170919210607j:plain

当日

当日スタッフだったので、事前に準備されたものの情報はあまりなく、当日は自分たちも新鮮な気持ちでの参加でした。実行委員長の長谷川さんに「お楽しみに!!朝からきてよ!!」とのお達しをいただいてたので何だ何だと楽しみにしていたのですが、三石琴音さんのナレーションで始まって朝から会場が湧きましたね。文化祭という表現が適切かはわからないですが、大人が本気を出した文化祭はすごい…。オープニングムービーのクオリティにも驚かされました。


iOSDC Japan 2017 スポンサー紹介

当日はTrackCのルーム担当と、相変わらずカメラを提げて写真業に励んでいました。登壇者さんの写真は主に専属カメラ部隊にお任せして、自分は主に会場や受付周辺、スポンサー様ブースの周辺を走り回っていました。部屋担当に関しては、司会、タイムキーパー、マイク持ち、Twitterチェック(空調とかピックアップ)、スタッフ間の連絡、満室時の対応などなど、でしょうか。少し気を抜くとニートになってしまいそうなほどに、スタッフの皆さんが臨機応変に素早く対応してくれていて、自分は結構ついていくのに必死でした(というのは内緒話)。皆さん本当にありがとうございました!

f:id:moshisora:20170916131053j:plain

f:id:moshisora:20170916131612j:plain

反省点として、個人的に気になってしまったのはやはり自分達のシャッター音で、オープニングのときにも注意書きがあったと思うのですが、スタッフ側も結構な数を撮影していたので邪魔になっていないかなーと(仕方ないとは思いつつも)少々心配にはなりました。こちらは持ち帰って考えてみます。

懇親会

Twitter等を確認していると「パックマンルール(輪になって話すときに一人分のスペースを空けておく)」が大変好評だったようで、自分も乗じてたくさんの方とお話できて楽しかったです。 そしてみなさんお気付きでしたでしょうか。懇親会のビールがめちゃくちゃ豪華でした。一枚貼っておきます。まさかここで「其の十」が飲めるとは…。

f:id:moshisora:20170916181717j:plain

写真

スタッフが撮影した写真(速報板)は少しずつGooglePhotosで公開していますので、ブログ、SNS等でご自由にご利用ください。

きちんと現像したものや、プロカメラマンによる写真も後日Flickrから配信予定なので、こちらも続報をお待ち下さいませ。

また、みなさんが撮影した写真も是非、こちらからアップロードください! https://photos.google.com/share/AF1QipOcKucEn3FYE7kS4dUnxU8Dw6VuIaqikNJnpbfiVV63LmYLtSZUdVMUSM40xSgGqQ?key=aWtJQlpXQndvaUtyQnN3c3V4RGZlVlA0aGpLWkRB

おわりに

企業/個人スポンサーの皆様のご支援、スピーカーの皆様のトーク、参加された皆様の協力、そしてスタッフの尽力で、最高だったなという一言につきます。皆様本当にありがとうございました。 やはり各カンファレンスそれぞれ色はあるとは思っていますが、中でもフェス感の強いカンファレンスだなーと実感しました(自分は後から知ったのですが、テーマが「フェス」だったんですかね)。本当に楽しかったのでまたあったら是非参加したいと思います。みなさんのブログにかかっているようなので是非お願いします!

あとは個人的な話ですが、趣味で写真を良く撮るので、趣味と趣味が融合して大変楽しかったというのもありました。大きいカンファレンスに限らず、カメラマンがご入用な方、お気軽にご連絡ください。飛んでいきます。

トークの話とか、夜のiOSDCの話とか、まだまだ書きたいことはありますが、長くなってきたのでひとまずこのあたりで…。

gulp-revのリビジョン付のファイル名を任意の形式にフォーマットするgulpプラグインを書きました

はじめに

ブサウザにファイルのキャッシュを捨てさせる目的でファイル名をリビジョン付きのものにする、ということは多々あるかと思います。 gulp でそれを行うためにgulp-revがよく使われていますね。
gulp-rev はファイルの内容をhashにしたものをリビジョンとして [ファイル名]-[revision].[拡張子] の形式で付与します。

今回は、やんごとなき事情により、 gulp-rev で生成されるリビジョン付のファイル名のフォーマットを任意の形式に変更したい、という状況になったので、 gulp-rev と連携して任意のフォーマットでリビジョンを付与できるプラグインを書きました。

www.npmjs.com

使い方

npmでインストールできます。

$ npm install --save-dev gulp-rev-format-re

以下のように、gulp-revの後に実行します。

const rev = require('gulp-rev');
const format = require('gulp-rev-format-re');

gulp.task('default', () => {
    gulp.src('./src/style.css')
        .pipe(rev())
        .pipe(format('[% rev %].[% filename %]'))
        .pipe(gulp.dest('./dist'))
        .pipe(rev.manifest())
        .pipe(gulp.dest('./dist'));
});

gulp-rev-format-re は、引数に与えた文字列の下記のパラメータを、それぞれオリジナルのファイル名とgulp-revの出力したリビジョンに置き換えます。拡張子は自動で最後に付与されます。

  • [% rev %] : gulp-revで付与されたリビジョン
  • [% filename %] : オリジナルのファイル名

上記の例では、出力されるファイル名はxxxxxxxx.style.cssのようにフォーマットされます。
前後や間に任意の文字列を挿入することもできます。

format('head-[% filename %]-prefix-[% rev %]-suffix')

とすると、head-style-prefix-xxxxxxxx-suffix.cssのようにフォーマットされます。

おわり

中身は大したものではない&やんごとなき事情で書いたのであまり利用ケースが思いつかない気はしますが、何かお役に立てることがあれば幸いです。
類似のプラグインとして、gulp-rev-formatがあるので、[ファイル名][prefix][revision][suffix].[拡張子]の順番を保ったまま任意のprefix suffix を追加したい場合はそちらを使うとよいのではないかと思います。

関連リンク

ISUCON6にmorimoto組で出場して準優勝でした

ISUCON6に出場しました

ISUCON6本戦@渋谷ヒカリエ LINE株式会社 f:id:moshisora:20161022090704j:plain:w480

ISUCON6に「morimoto組」で出場して、準優勝できました。メンバーは、

という布陣でした。言語は自分とボンゴレ氏がPerlの方が使い慣れている関係でPerlを選びました。

スコア推移

ラスト1時間は他のチームのスコアが非表示ですがこんな推移でした。一台でチューニングしてから最後に複数台構成になったので結果発表ではダークホース感があったかもしれません…。

f:id:moshisora:20161024182157p:plain

Time PASS/FAIL Score 変更
11:02:53 PASS 942 Perl初期スコア
11:21:28 PASS 1159
11:58:31 PASS 1364
12:31:15 FAIL 253
12:36:34 PASS 1616
12:39:18 PASS 2051 nginx導入
12:43:40 PASS 3175 perlのプロセス数を200に(していたらしい)
12:46:41 PASS 3543
12:49:26 FAIL 12
12:53:18 PASS 2816
13:03:36 PASS 3314
13:35:14 FAIL 251
13:47:46 FAIL 251
14:07:14 PASS 3211 pointsをstrokesのカラムにjsonで保存するように
14:09:41 FAIL 0
14:10:56 FAIL 0
14:11:25 FAIL 0
14:14:25 PASS 3470
14:15:21 FAIL 0
14:16:38 FAIL 0
14:20:25 FAIL 0
14:24:03 PASS 4697 /img/* で返すSVGPerlレンダリング
14:27:05 FAIL 252
14:29:53 PASS 4781
15:17:25 FAIL 106
15:21:32 PASS 7412 /img/* をRedisにCache
15:31:52 PASS 6040
16:09:04 FAIL 253
16:10:24 FAIL 253
16:12:32 PASS 8020
16:54:56 FAIL 253
16:58:52 FAIL 253
17:10:31 PASS 9339 /api/stream/rooms をRedis pub/subとGo実装に置き換え
17:41:33 PASS 15241 2台構成
17:43:36 PASS 30714 5台構成
17:45:12 PASS 29301 isu02再起動試験
17:48:35 PASS 36067 isu01にnginx,MySQL,Redis、その他でPerl,Go,Reactの5台構成

タイムライン

10:00~12:00

開始間も無くしてAzureへのデプロイがうまくいかなかったり、アプリを読んだり触ったり、Perl実装を反映してベンチをかけるまでに1時間ほどかかりました。その後手元でperlのサーバを動かしたりと準備をしているといつの間にかお昼頃に。

12:00~15:30

pointsをstrokesのカラムにjsonで保存

points は一点ごとにレコードになっていて、必ず strokes と一対で、かつ更新もなさそうだったので、これは strokes と一緒にしてしまっていいよね、ということで strokes テーブルにカラムを追加して、そこにjsonで入れることに。同時に、初期データもjsonに変換するスクリプトを書いて strokes のカラムに保持、としたもののこの時点ではあまり効果は無し。

/img/* で返すSVGPerlレンダリング

SVGの生成はReact側からperlAPI経由で pointsjsonを取得して、React側で表示するSVGを生成していたので、これをperl側で生成するようにする実装が、残り2名で points の変更を入れている間にfujiwaraさんにより導入。

/img/* をredisにcache

stroke が新たに追加されるまで、 /img/* の結果は更新する必要がないので、これをredisにcache。新たに stroke が追加されたときにキャッシュを破棄するように。これで結構伸びて7412。

15:30~17:10

この時間が苦しかった…。

/api/stream/rooms をRedis pub/subとGo実装に置き換え

200プロセスでゴリ押していた stream がやはり辛いという話になり、この部分の実装をGoに置き換えることに。Perl側から新しいstrokeの投稿があったときにstrokeをpointsと一緒にpublish、Go側でsubscribeしてクライアントに配信するように変更。この変更でPerlでは500msごとに新しいstrokeをまとめて送信していたものが、新しいstrokeが来るたびに送信できるように。一旦は完成したかと思いきや、ベンチを走らせると 必要なwatch_countが送信されていません 旨のエラーメッセージでベンチが通らず、しばらくこれの解決に食われましたが、ボンゴレ氏が、「これ、publishされなくても500msしたら返さないといけなくないですか?」と閃いてなんとかベンチ通過。パフォーマンスも改善して9339。

新しいstroke投稿があったときにすぐ送信できる実装自体はあまりスコアに影響がないらしかったですが、触ってみるとレスポンス早くなっていいアプリになった感じはありました。余談です。

strokes countもredisにcache

毎回 stroke を全部拾ってきて scalar で個数だけ拾っている部分があった

$room->{stroke_count} = scalar @{ get_strokes($self->dbh, $room->{id}, 0) };

ので、これもredisに

my $stroke_count = $redis->get('stroke_count'.$room->{id});
unless ( $stroke_count ) {
    $stroke_count = scalar @{ get_strokes($self->dbh, $room->{id}, 0) };
    $redis->set('stroke_count'.$room->{id}, $stroke_count);
}
$room->{stroke_count} = $stroke_count;

stroke 投稿時に incr: $redis->incr('stroke_count'.$room->{id});

というものは書いてはいたものの、時間が差し迫っていたのでmergeしないまま複数台構成作業へ。

17:10~18:00

コードはフリーズして、複数台構成と再起動試験。
fujiwaraさんにお願いしてあとは自分たちは祈るだけという状態に。
最終的には isu01 でnginx, MySQL, Redisを動かしてそれ以外でPerl, Go, Reactをのせました(競技後に教えてもらいました)。

isu02 の再起動試験をしたのち、最終的な構成にしてスコアは36067。最後に isu01 を再起動はしたものの、これ以降スコアが伸びることもなさそうだったのと、時間が終了5分前くらいだったこともあり、これでスコアfixしましょうということに。ベンチは実行しないまま、各自 isu01 につないでちゃんとレスポンスが返ってることだけ確認してそっとコンソールとブラウザを閉じました。

結果

最終スコア 36067 で準優勝でした。

f:id:moshisora:20161022190409j:plain:w320

反省はやはり、コードくらいは2人でがんばるぞ!!で、fujiwaraさんに下回りをお願いするのが理想形だったと思うのですが、結局結構コードも書いてもらってしまったことと、Go慣れしてなくて選択肢も最初から絞り気味だったことでしょうか。お題を聞いた時点でPerlつらいしGoよさそうという話はあったのですが。golang書こう…

結果は嬉しいものでしたが、ブログを書きながら振り返ると、肝心なところは全部先輩頼りでしたし、16時を過ぎたあたりからは何やれば良さそうかも分からなくなってきて、個人的にはやはり全然戦えてないなーという感想で、悔しくも感じた初参戦でした。悔しさ胸に勉強せねば…

出題・運営の皆様、予選も本戦も、楽しい問題と競技を本当にありがとうございました。また協賛の皆様、楽しい懇親会をありがとうございました!
そしてチームの皆様、本当にありがとうございました!すごい体験をさせていただきました。嬉しくも悔しくもありましたがこれを糧にまた頑張ります。

最後にこちらを!

ISUCON6にmorimoto組で参加してきました

ISUCON6に参加しました

ISUCON6 、日曜日に参加しました。前々から出てみたいなーと思ってはいたものの、面子をゆるぼしてみたものの集まらず…と見送りかなーと思っていたところ、社内メンバーで組めることになって自分でも驚きの布陣で参戦することに。ありがとうございました!

  • morimoto組
    • fujiwara(組長。いわずもがな)
    • moshisora(私。新卒2年目。初参戦)
    • ボンゴレ(新卒1年目。初参戦)

足回りや方針はfujiwaraさんに引っ張っていただきつつ、残る2名で実装がんばるぞい、という感じで挑みました。実装言語はPerlでやりました。

事前準備

  • slackとかgithubリポジトリ準備
  • 過去問とpixivさんの社内ISUCONの問題をやってみる
  • Azureさわってみる
  • (項目は作ってみたものの特別なことはしてなかった…)

当日タイムライン

当日のスコア遷移をメモしてなかったので朧げですが、slackとgithubのログを掘り返しつつ、こんなことをしましたメモです。アプリ側しかみてないので覚えていることをさらっと。

9:00

  • 集合&作業場設営
    全員起床成功。
    会議室を確保してなかなかよい環境でできました。

10:00〜11:00

  • Azureにdeploy、chefを回して環境準備。30分くらいかかった。
  • dumpとかbackupとかしておく。
  • (たぶん)ミドルウェア類設定を反映。
  • アプリケーションをさわってなんとなく把握。

11:00〜12:00

  • ログとか計測とか。
  • コード読む。
  • isutarをisudaに統合。
  • ローカルで動かないと辛いですねーと環境を作り始める。

12:00〜15:00

スコア伸びるまで結構つらい時間でした。

  • とりあえずindex alter table entry add index updated_at(updated_at);
  • トラップを踏んだのか何なのか、新卒陣がPerlのローカル環境を動かせずに断念。以後「動け!!!🙏」と念を込めてプルリクすることに。ちょっと凹んだ。
  • 明らかに htmlify が重いので、とりあえず Regexp::Trie を導入するもあまりスコアは伸びず。
  • 毎回生成してるから重いのでは…ということで entry のidを覚えておいて更新があったときだけ再生成するように変更。

(この時点で最大スコア20000程度。いまいち伸びず内心結構焦ってました…。)

  • 正規表現をRedisに入れるようにして、 htmlify正規表現を作るのはやめて、 entry がpostされる時に再生成するように変更。が、同時リクエストでRedisの値の整合性が取れずベンチがこける。
  • post entry用のプロセスを立ててnginxから対象のパスは専用のそちらの1プロセスでさばくように変更。この変更で15時前頃にスコア80000くらい。一気に上位に食い込んでやる気が…!

  • この頃に /SELECT COUNT(*) FROM entry していたところもRedisにのせましたが、スコアは若干上がった程度でした。

このあたりから、プロセスを生かしたまま2度目のベンチを回すとスコア120000くらい出ることに気付きながらも原因についてはこの時点ではよくわからず。
同時に、 "上町村" に "川上町" からのリンクがありません (GET /keyword/川上町) というメッセージが出始め、「なんでしょうねこれ」とはなりながらも、運営へ質問したところ Status: PASS ならスコアは有効ですとの回答を得られたのでとりあえず無視。ぬか喜びしていた時間帯。

15:00〜17:30

スパムチェッカーがCPUを食っていてしばらくワード等ログに出して見てはみたものの、NG判定されているワードも規則が見えなかったのでここのテコ入れは断念。
あまり突き刺さる変更はできなかったものの地道にスコア更新。細かいものも色々。

  • /entries のクエリを自己結合に。
  • /stars をRedisにキャッシュ。
  • initializestars を全部Redisに載せて get_multi するように変更。
  • uri_for が遅いらしい&絶対パスにしなくてもベンチ通る。ので全部消す。(たぶんほとんど効いてない)
  • user_nameもsessionにいれて set_name のクエリ削除
  • とかとか

ここまでで初回ベンチスコア90000くらい、何度か回すと最大スコア170000くらい(まだ謎でした)。

17:30〜18:00

post entry用に立てたプロセスをrestartするとスコアが90000程度に戻ってそうだと発覚。どうやら Trie オブジェクトが生きたままになっていて、1度目のベンチで追加したキーワードが2度目のベンチでも効いていた模様でした。ここでようやく謎が解けた。30分前。
(なので、ないはずのリンクが作られていた模様。川上町の謎が解けた…)

ならば、ということで、一度ベンチを走らせて追加されるデータをテキストに落として、 initialize 時に正規表現に追加しておくという実装がfujiwaraさんによって高速挿入されてコード変更は終了。完全に未来予知作戦なので本質的ではない&データセットが変わると全く効かないので、複雑な気持ちですねーという話をしながらではありつつも、これで140000点を突破(17:50頃)。
その後再起動してベンチキューに滑り込み、終了3秒前にベンチ実行開始。稼働状況を見る感じ大丈夫かなあ…という雰囲気でしたが、再起動後のスコアを確認できないまま競技時間終了…。

ギリギリもギリギリだった…

予選突破できた

不安な感じで競技終了時間を迎えてしまったので反省会中もdkdkしていましたが、最終スコア143,366で無事通過できました。とはいえ最後の裏技的なやつが効いてなかったら9万点いくかいかないかくらいだったので、結構やばかったんじゃないかなと…。

  • Perlのコード書く以外やってない。要復習。
  • htmlifyもっとなんとかできた気もする…。
  • 手元でPerl動かそう…(重要)
  • 普段やってないことをやると結構単純なことで詰む。全部一瞬で拾ってくれる大先輩心強すぎる…。ありがとうございました。(自分でできるようにならないと…)
  • (普段仕事でJSばかりなためか、JSのコード書いてボンゴレ氏に「それJSですよ」って突っ込まれた。手が…手が…)

大会の製作・運営のみなさま、ありがとうございました!初参加でしたが楽しかったです。ベンチマークも数分で実行されて快適でした!

そしてチームの皆様、本当にありがとうございました!ものすごく連れて行ってもらった感が否めないですが、何はともあれ予選突破できました。突破されたみなさま、本戦でお会いしましょう!!本戦は勉強じゃない、戦争だぞ!!との激励もいただきまして、一ヶ月頑張ります。がんばります…

さいごは反省会の様子です。弊社golang実装組がなかなかつらそうでした。正規表現

リンク

sfujiwara.hatenablog.com