はらへり日記

腹に弾丸

ISUCON6予選に出た

結果

正確なスコアはメモし忘れたんですが、最高点が15000点ぐらいでした。

後半はずっと14000点を前後してた

何をしたか

自分用に覚えてる範囲で軽くメモ。

  • まずalp, pt-query-digestを仕込んでベンチを叩く。スコア200くらい
  • DB周りの@aboyとミドルウェア、OSの@ktarow、アプリの自分で各々予め決めてたことをやる
  • アプリを最初に触ってみる。はてな感すごい
  • DBのデータ量が少ないこと、データ数もテーブルも少ないことに戸惑う
  • 2つWebサーバが立ってることにとまどう
  • とりあえずインデックスを貼ったりnginx周りをチューニングしてもらったりDBにメモリ振ったり
  • 2つのWebサーバ間でお互いを叩いていて無駄だったので消す

ここまででちゃんとアプリケーションコードを読み切れていなかったり、作戦会議の詰めが甘くてFAIL地獄にハマる。

  • 昼頃にFAIL地獄を抜けて11000点くらいになる
  • この時点で去年のスコア(一概に比べられないけど)の10倍以上だったのでハイタッチする
  • ここからインフラのやることがあまりなくなってくる

メモリを食わせてもスループットがあがらなかったり、はちゃめちゃに遅いクエリが無くてここらへんでやっときちんとアプリコードを精査する。おそすぎた。

  • entry本文を表示するために毎回7000語で正規表現をかけていることに気づく
  • ハッシュへの置換とハッシュからリンクへの置換を一挙にやろうとしたがやってみて失敗したことに気づく

例えば1ってkeywordがあると、<a href="/keyword/%10"></a>みたいなリンクがあった時にリンクの内容を置換してしまう。

よく考えればわかることだった…

  • ここらへんからこまめにRedisにデータを突っ込んでいく
  • 突っ込んだ後、それを利用できるものは利用するよう書き換えていく
  • なんやかんやでちょっとずつスコアがあがり15000点になってから2時間ぐらいエラーと戦う

という感じでした。超雑。

なんか知らないけど平尾山のページでめっちゃエラー指摘されました。もうしばらく平尾山は見たくない

所感

木曜から休暇取ってたのでずっとISUCON5の予選で練習してたから頭がDB脳だったのがあまりよくなかった。

始まってみるとDBで改善できるところが少なすぎて結構戸惑った。

去年の反省生かして最初の30分くらいは何もせずにアプリコード読んでたけど、1時間半くらいかけてもよかったかも。

きちんと読み切った上でガッツリ作戦立てたほうがよかった。全部Redisにのっけるとか。

あと、終わって冷静になってから気づいたけど

  • htmlifyの内容を全てキャッシュ
  • POST /keywordが来たらDBからそのキーワードを含むものだけ探してキャッシュを作り直す(Twitterでやったって人見た)
  • ただ、リクエストの中でキャッシュ作り直すとレスポンス遅すぎて減点が痛いのであらかじめPHPでデーモン立ててそこに食わせるようにすればよかった

って思った。ただ、デーモン立てるっていってもそのスクリプト用意してなかったし今度から用意しようと思った。

ループ見たときはPHP並列できない…つらい…ってなってた。

トラブル

東新宿コワーキングスペースでやったんだけど、途中で充電器抜いたらスパークして死ぬかと思った。

f:id:sota1235:20160919013246j:plain

すごい音なったしISUCON中に死ぬとこだった。まじで。

反省会

盛大に反省した。

f:id:sota1235:20160919013356j:plain

悔しいポイントは本当に多くて、予選突破まで行かなくても人権が得られる得点には届きたい人生だった。

ただ、去年は本当に無知で何もできなかったけど今年は終始頭を使えてたので成長を感じた。

何よりスコアが15倍ですからね!!!!!!!!!!!

来年もきっとあると信じて引き続き精進したい。

まとめ

エンジニアになるために人権勝ち取っていこうな。

npm scriptsでエラーログを表示させたくない話

npm run hogeでエラーを出したくない

eslintでのチェックやトランスパイルの実行は下のような感じでpackage.jsonに書いてnpm run lint等で実行するようにしてる。

{
  "scripts": [
    "lint": "eslint src/",
    "build": "babel src --out-dir dest"
  }
}

その際、eslint実行等の場合はコマンドの実行自体がコケるだけでnpmが鬱陶しいエラーを出してくる。

npm run lint実行結果

欲しいのはnpm scriptの結果で、下のはnpm初心者には無益で紛らわしいログでしかない

これをどうにかしたい

解決策を調べる

どうにかできないか調べてみた。

解決策その1:--silentオプションをつける

npmにはloglevelという概念があり、これにオプションを指定するとログの出力形式を変更できる。

docs.npmjs.com

その中の1つに--silentがあるのでnpm scriptを実行する際にこのオプションをつける。

省略形で-sでもよい。

--slientオプションつきnpm run lint

これで鬱陶しさはなくなったが

  • 毎回オプションをつけなければいけない
  • npm-debug.logを吐き出さないので本当にnpm由来のエラーが発生したらオプション外してもう一回実行しなきゃいけない

というデメリットがある

解決策その2:.npmrcを設定する

最近こんな記事を読んだ。

qiita.com

今まで知らなかったのが本当にもったいないくらい最高の話で、ローカルにnpmコマンドの設定を保持できる。

なので.npmrcを作成し、以下の用なオプションを指定すると常時、loglevelsilentになる

loglevel=silent

これで毎回オプションを足す手間は省ける。

ただし、これにもデメリットがあって

  • CI等でこうするとエラーが発生したときにログが読めない
  • npm-debug.logが吐き出されない

という問題が依然として残る

解決策その3:aliasでごまかす

後にも言うけどこの問題に根本的解決策は現状ないです。

なので上記デメリットを吸収できる方法は思いつく限りだとshellのaliadを指定する方法です。

要は単純で、alias名は何でもいいんだけど例えば下記のようなものを各々のdotfilesに追記する。

alias npm-run='npm run --silent $*'

これでnpm scriptsを実行したいときはnpm-run lintとかで実行する。

ログが欲しい時やCIではnpm run lintを使う。

解決策番外編:pipeで無理やり成功させる

これは全然解決策じゃなくて、絶対にやめたほうがよいのであえて書いた。

何かというと、npmのissueやstackoverflowを眺めてると「npm scriptに|| true足せばいいよ」ってのがあって

npm scriptの実行結果を無理やりtrueに持っていけばエラーじゃないからログも出ないぜという話。

{
  "scripts" [
    "lint": "eslint src/ || true"
  ]
}

ただ、確かにログは吐かなくなるんだけどnpm scriptの実行が失敗してもコマンドがこけないので

CI等で実行してる場合は例えばeslintが失敗してもそれを補足できなくなる。

絶対にやめような^^

結論

現状、「標準出力にいらんエラーを吐かせず、npm-debug.logはちゃんと残してくれるnpm scriptの書き方」はない。

ので以下の2策に逃げるしか無い気がする。

  • npm script実行時は-sオプションをつける
  • .npmrcloglevel=silentを指定する

ただし、前者は複数人開発だと周知が面倒だし後者はいざというときにログが無くて死ぬ可能性がある。

npmのissueにもこの話題はあがっていて、dev環境用のnpm scriptsを用意しようとかいろいろ提案されているみたい。

run-scripts are too noisy while used in development · Issue #8821 · npm/npm · GitHub

個人的にはnpm慣れてるので、個人ではshellのaliasで逃げつつチームの時はこのブログ記事ぶん投げようかなという感じ。

追記

匿名の方より以下のコメントをいただきました。

f:id:sota1235:20170127155929p:plain

これであれば「標準出力にいらんエラーを吐かせず、npm-debug.logはちゃんと残してくれるnpm scriptの書き方」を実現できそうです。

具体的にはこんな感じのshellを書いて使う。

#!/bin/zsh

###
# npm run with slim
###
DOTFILES_NPM_ERROR_LOG="$HOME/.dotfiles/dist/npm_error.log"

function npmrun() {
  npm run $1 2>$DOTFILES_NPM_ERROR_LOG

  if [ ! $? -eq 0 ]; then
    echo "npm error log recorded at $DOTFILES_NPM_ERROR_LOG"
    return 1
  fi
}

もしかしたらもっと良い書き方があるかもだが、これでひとまず解決した。最高!

YAP(achimon)C::Asia Hachioji 2016に登壇しました

YAPCに登壇したぞ!

30分のトークとLTでそれぞれ登壇しました。

10分以上の発表は実は初めてでした。

年初のポエムで立てた対外発表の第一歩として応募して採択されたので頑張った。

sota1235.hatenablog.com

DIコンテナの話

DIの発表はよく見るけどDIコンテナの話はそんなに見ない気がしたので発表した。

YAPCのPはPHPのPだと聞いていたのでPHPのDIコンテナに絞って発表した。

speakerdeck.com

ESLintの話

LTではJavaScriptの話した。

30分トークで死ぬほど緊張した後だったのでどうにでもなれ精神で頭おかしい発表してしまった。

かわいいESLint実装誰か頼む|ω・`)

speakerdeck.com

反省会

  • 発表テーマがちょっとわかりづらいだけどに聴衆が少なかった。(そのおかげであまり緊張しなくて済んだのだけど)
  • 接続確認が甘くてスライドの下が見切れてた
  • 日本語が怪しい
  • LTそこそこ笑い取れて安心した

とはいえ、聴いてくれてた方はみなリアクションをくれたしご質問ももらったので最初にしてはまずまずかなと思ってる。

残念ながら2日目は参加できないのですが、参加される方は楽しんでください!!

今後

次はPHPカンファレンスに登壇したい!頑張ります。

社内で1年ぶりにXSSの話した

1年前

だいたい1年前、社内でXSSの認知を広めるべくXSSの話をわからないなりにしました。

1年経ったのでもう一回、社内で発表しました。

XSSの無い最高の夏を過ごそう

ほんと、タイトルの通り。 今回は座学より実践形式に重きを置いていて、いくつか例題を作って実際にみんなに触ってもらいました。

かなり時間かかったけど、解けなくて悔しがってる人が何人書いたようで攻撃サイドの気持ちになってもらうっていう意味ではとても良かったように感じました。

XSS Tour

CTFほどじゃないけどそれらしきエセサイトをいろんな問題(過去の解けなかったセキュキャン応募用紙の問題やGoogleXSS Gameあたりを参考に)を参考に用意しました。

リポジトリも公開してるので興味のある方はどうぞ。

github.com

JSのArrayBufferがよくわからなかったのでほんの少しだけ調べた

前提

初心者なりの超浅い理解です。

(というのも、元々Web Audio APIに食わせるためだけに調べてたので…)

ArrayBufferとは

ひとことで言うと「JavaScriptバイナリを扱う」ために生まれたという認識。

なので画像処理等に使われていることが多い印象です。

少なくとも僕はWeb Audio API以外で使ったことがない(´・ω・`)

ArrayBufferは以下のようにインスタンス生成することができます。

const buf = new ArrayBuffer(30);

普通にクラスみたいにnewします。引数には確保したいバイナリの長さを渡します。

この瞬間、何が起きるかというと指定した長さのメモリ空間がバイナリ保存用に確保されます。

なので当然、アホみたいにでかい数字を渡してインスタンス生成しようとすると「メモリ足りないぜよ!」となってエラーになります。

また、このArrayBufferにはもう1つの特徴があり、直接読み書きすることができません。

以下のドキュメントを見てもそれらしきAPIやプロパティが存在しないことがわかると思います。

ArrayBuffer - JavaScript | MDN

せっかくバイナリを保存するためのメモリ確保してもなんにもできないやーん、となってしまいます。

そこで出てくるのが型付き配列というものです。

型付き配列(Typed Array)

型付き配列とは、その名の通り型を持つ配列のことです。

通常のArrayとは違うものですが、通常のArrayのように使用することができます。

TypedArrayにはいくつかのサブクラスが存在し、実際にはそれらを使用します。

では実際、こいつが何の役に立つのかというとArrayBufferの確保するメモリ空間にアクセスするために使用します。

具体的には以下のような感じで使えます。

const buf  = new ArrayBuffer(100); // メモリ空間の確保
const view = new Uint8Array(buf);  // 1行めで確保されたメモリ空間へアクセスするための型付き配列

これでview変数を通じてbufで確保されているメモリ空間にアクセスすることが可能になります。

(Uint8Arrayバイナリへのアクセス方式を8bit整数(符号なし)で行いたい時に使用する型付き配列のサブクラスの一種)

これで例えば以下の様なコードを書くとbufを書き換えているのが同義になります。

view[0] = 0x12; // bufの0番めに書き込んでいるのと同義

このためのTypedArrayだったんですね~~~

まとめ

  • ArrayBufferはバイナリ保存用にメモリ空間を確保するためのもの
  • 直接の読み書きは不可
  • ArrayBufferに格納されるバイナリを読み書きするのにTypedArrayを利用する
  • TypedArrayはだいたい配列と同じノリで扱えるよ!

参考

Javascript typed arrays - JavaScript | MDN