あんパン

こしあん派

ISUCON6予選やった

Iikanji Speed Up CONtestというのが毎年あって、それの第6回の予選に出た。2日目日曜日の方。去年も出ていたのだけど、全く意味わからなかった。今年は少しマトモで、決勝こそでられなかったもののそこそこのスコアになったので満足といった感じ。これくらい↓

f:id:masawada:20160920225037p:plain:w200

一つ情報を追加しておくと、前日の夜くらいまで38度近い熱があって死んでいた。当日の朝に36度台に下がって、ISUCON終了ごろには37.5度というステータスだった。

それはさておき僕がやったことは以下4点。なお言語はPerlです。

  • isutarのisuda統合
  • キーワードリストをmemcachedに載せる
  • ページングをmemcachedでやる
  • is_spam_contentのレスポンスをキャッシュに載せる

isutarのisuda統合

ぱっと見たときに、isuda.psgiとisutar.psgiがあって最悪な気持ちしかなかった。幸いにもisutarにはマトモな仕事をしているエンドポイントが2つしかなく、テーブルもstar1つのみだったので、まるまるisudaに移動した。$c->dbh$self->dbhに書き換えるとかそういうのはあったけどほぼまるまるコピペ+Furl使ってるところを直接DB見に行くようにするのと、isutarDBのテーブルをisudaDBに作り直す、くらい。show create table star\G;みたいなの発行するとそのままCREATE TABLE文が出てくるのでそれをisudaで発行しただけ。30分もかからず終わったと思う。

キーワードリストをmemcachedに載せる

htmlifyを叩く度にキーワードリストを生成するために

SELECT * FROM entry ORDER BY CHARACTER_LENGTH(keyword) DESC

でエントリのリストを生成してからmap { $_->{keyword} } @$keywordsみたいなことしててとにかくDBの帯域を食っているイメージだったので、新たにキーワードを登録する度に全キーワードを文字数降順でソートした配列を再生成してmemcachedに載せておいて、それを使うようにした。これは意外と効いてスコアが2700くらいから一気に1万超えをはたした。

ページングをmemcachedでやる

一般にMySQLのOFFSETは遅い。実際に50ページ目くらいのリクエストのmean response timeが結構大きくなっていたので試しにやってみようみたいな話になった。上のキーワードリストと同じようにupdated_atの降順でキーワードリストを生成しておいて、そこからページャを生成したりした。これはあまり効果なかった気がする(2000くらいは上がった)

is_spam_contentのレスポンスをキャッシュに載せる

キーワード登録時にisupamにコンテンツがスパムかどうかを問い合わせていそうだった。is_spam_contentの中でFurlでhttpリクエストを発行していて、これが無駄だなーと思っていたので試しにis_spam_contentの引数のsha1_hexとisupamのレスポンスの組をキャッシュしたところ、一気にスコアが40,000超えするようになった。 isupam自体はどうせ特定の単語に反応してvalid/invalidを返すだけだろう(しかもその単語はそれほど多くない)という予想をしていたし、isudaに突っ込まれるテキストもWikipediaから引っ張ってきたいくつかの記事だけだろうという予想もあったのでやってみたところ異常な効果があった。しかもベンチの回を重ねるとスコアが伸びてよかった。最終的に65000くらいで頭打ちになったけど、だいぶ効果があったのではと思う。

そのほか

こうやってみるとキャッシュ載せるくらいしかやってないな… 結局いろいろやったけどキーワードリストをmemcachedに載せるのとis_spam_contentのレスポンスをmemcachedに載せるのくらいしか効果はなかった気がする。正規表現のところボトルネックになってるだろうなーと思いつつこれ時間内にほぐす自信ないなという気持ちであまり手を入れなかったけど、普通にもう少し手を入れたら良かった気がする。そうしたらもうちょいスコアあがった気もする。

とはいえ実は最初の3時間くらいは初期スコア0に悩まされてなにもしてなくて、最後の30分くらいは再起動テストでなにもしていなかったので、実質の作業時間は4時間ちょっとだった。時間的にもあまり余裕はなかったので、来年はこんなことのないように頑張っていきたい。今年全然なにもしてなかったけどアプリケーションのプロファイルとかもとったほうが良さそうですね。。

全体を通して、普通のWebサービスじゃ絶対にやらないでしょみたいな雑な対策/雑なコードを量産してあれくらいのスコアまでいくのかーという知見が得られて面白かった。Perlでmemcached扱う方法とか学べてよかった。

さいごに、一緒に戦ってくれたid:masayoshiくんid:taketo957くんありがとうございました、最高すぎた。来年もできればお願いしたいです:pray:

YAP(achimon)C::Asia Hachioji 2016 mid in Shinagawa ネット担当を支える技術

7/2, 7/3は YAP(achimon)C::Asia Hachioji 2016 mid in Shinagawa でした。ぼくはネット担当(Twitter担当)としてスタッフをしていました。

今回は快適にTwitterを監視する技術を紹介します。

ネット担当とは

各担当の仕事についてはGitHubのWikiに記載されています。

github.com

当日の業務としては

  • 各部屋の写真を流す
  • 飛び入りトラックの情報を流す
  • Wi-Fi情報や受付で困っている人がいたら声をかける
  • 落とし物情報を流す
  • 資料アップロード報告などのツイートをRT
  • そのほか諸情報を流す
  • Togetterでまとめる

という感じでした。主にTwitterに張り付いている仕事です。基本的に張り付いていて1日目は1つもトークを見ておらず、2日目に2,3本だけ見ました。(が、一番聴きたいトークを聴けた&&ちょっとしたうれしいことがあったので最高でした)

このTwitter監視業務をするに当たって便利なツールを作成したのでご紹介です。

作ったもの

特定のハッシュタグ/単語を含むツイートをSlackに流すアプリケーションです。Herokuにデプロイできます。

github.com

特にREADMEとか書いてないんですが、適当にポチポチすると完成するので特に解説不要だと思います。

なぜSlackに投稿するのか

特定のワードで検索しながらTwitterを眺めるだけであれば、TweetDeckでも良いと思います。しかし、以下の点でTweetDeckでは不十分でした。

  • Win/Mac/Linuxで使えるようにしたい
    • 何が起きても最悪ブラウザなどで見たい
  • モバイルでも同じ環境がほしい
  • ツイートを通知したい

本来であれば専用にUIを用意してSocket.IOなどでリアルタイムに表示して監視するのが筋だと思います。しかし、ただ監視するために無駄に凝ったものを作るのは達成したいことに対してあまりにも手間がかかりすぎます。そこで、Twitterで特定のキーワードを拾ってSlackに投稿するようにしました。

Slackに投稿することにより、以下のようなメリットも生まれました。

  • 未読管理ができる
  • お問い合わせなどにreactionをつけると誰かが対応したことがわかって便利
  • 特定キーワードのみをハイライトした上で通知できる
  • 難しい実装を考えなくてよくなる
    • データベースなどを考えなくても過去のツイートをさかのぼれる
    • XSSなどを考慮する必要がない

特に、未読管理とキーワードのハイライトは役に立ちました。

懸念点と実際の運用

Herokuにデプロイボタンを用意していますが、実運用はVPSで飼っているdokkuに任せました。

運用にあたって、以下が懸念点でした。

  • ハッシュタグをつけてくれる人がどれだけいるのか
    • statuses/filterを利用していたので、ファジーな検索ができない
  • Slackのrate limitに引っかからないか
  • 無料枠でさかのぼれる10000件を超えないか

結局のところ流量は異常に速いということもなく、2日間安定して運用することができました。当日朝まではハッシュタグを使用している人はほとんどいませんでしたが、開始以降はだいぶ使われており、ヤパチーに関するほとんどのツイートを拾えていたのでは(?)と思います。また、無料枠の点に関しては、運営用に使用していたチームとは別のチームを作成してそちらに投稿することで、運営側に迷惑をかけることなく利用することができました。

実際に活用していたのは自分だけだったのですが、特定キーワード(資料アップロードなど)を監視して通知することで割と素早く反応できたのではないかと思います。

f:id:masawada:20160703235305p:plain

こういう雰囲気でした。ツイートのリンクを表示しておくことで、 PCではTwitterWebから、モバイルではTwitterAppから反応することができてだいぶ便利でした。

まとめ

という内容を飛び入りトラックで5分くらいつかってやろうと思っていたのですが、あまりネタにならないなーと思ったのと、単純に睡眠不足で疲れていてスライドを生成できなかったのでここに公開します。普通に使っていて便利だったので簡単な監視ツールとして是非ご利用ください。

git diffで差分のあるファイルを一瞬で開くやつ

編集してまだgit addしてないやつをもう一度開きたいとき,いちいち探るの面倒なので作った.多分この世に既にごまんとあると思う.

gitのサブコマンドとして作るならこんな感じ

FILE_PATH=$(git diff --name-only | peco)
if [ ${#FILE_PATH} -ne 0 ]; then
  $EDITOR $(git rev-parse --show-toplevel)/$FILE_PATH
fi

zshの関数として作るならこんな感じ

function peco-git-editdiff {
  local dir=$(git diff --name-only | peco)
  if [ ${#dir} -ne 0 ]; then
    BUFFER="${EDITOR} ${dir}"
    zle accept-line
  fi
  zle clear-screen
}
zle -N peco-git-editdiff
bindkey '^e' peco-git-editdiff

ISBNから書名と著者名とるやつシュッと作った

調べながら30分くらいでシュッと書いた.あとでもうちょい綺麗にする.

検索には国立国会図書館サーチのAPIを使った.

require 'rexml/document'
require 'csv'
require 'rest-client'

# usage:
# bundle exec ruby search.rb source.csv > result.csv

table = CSV.table ARGV[0]

table.each do |row|
  res = RestClient.get "http://iss.ndl.go.jp/api/sru?operation=searchRetrieve&query=isbn=#{row[:isbn]}"
  xml = REXML::Document.new res.to_str

  record = REXML::XPath.match(xml, '/searchRetrieveResponse/records/record').first
  data = record.elements['recordData']

  row[:title] = data.to_s.match(/<dc:title>(.+?)<\/dc:title>/)[1]
  row[:author] = data.to_s.match(/<dc:creator>(.+?)<\/dc:creator>/)[1]

  sleep 2
end

print table.to_csv

ネームスペースが登録されてなくてdc:titleみたいなタグ抜くの大変だったので雑に正規表現でやった.

↓こんな感じでCSV書いてsoruceとして指定すると良い

isbn,title,author
9784873113074,,
9784873115658,,

こんなのが出力される

isbn,title,author
9784873113074,エンジニアのための時間管理術,"Thomas A.Limoncelli 著,クイープ 訳"
9784873115658,リーダブルコード : より良いコードを書くためのシンプルで実践的なテクニック,"Dustin Boswell, Trevor Foucher 著,角征典 訳"

ワイ

iTunes音楽同期事情を知りたい

いま業務と私物でPCが分かれてる.曲は私物PCに入ってるので,会社に行く時に毎日持ち歩いてる.iPhoneで聴くこともできるけど,選曲の度にiPhoneを開くのが苦痛なので,PCで聴いている.業務PCと私物PCで曲を同期できれば私物PCを持ち歩かなくて済む.

特にこだわりはないのでプレーヤはiTunesを使ってる.iTunesの曲はSDカードスロットに刺さったストレージに蓄えるようにしている.iTunesだったらiCloudミュージックライブラリあるやろという人もいるかもしれないけど,あれはライブラリを激しく壊すので一生使う気がない.あまりに壊れるので一時期大分怒ってた.

とはいえいまのところ妙案が思いつかなくて,rsyncでUSBメモリを経由してうまく同期するようにしてる.法的に問題なくあまりコストのかからない良い案あったら教えてください.


いまこんな感じのシェルスクリプト書いてスクリプト置き場においてある.

backup.sh

#!/bin/bash

# /Volumes/Music/iTunes -> /Volumes/MusicMaster/iTunes.files
# /Users/username/Music/iTunes -> /Volumes/MusicMaster/iTunes.conf

rsync -avh --progress /Volumes/Music/iTunes/* /Volumes/MusicMaster/iTunes.files
rsync -avh --progress $HOME/Music/iTunes/* /Volumes/MusicMaster/iTunes.conf

restore.sh

#!/bin/bash

# /Volumes/MusicMaster/iTunes.files -> /Volumes/Music/iTunes
# /Volumes/MusicMaster/iTunes.conf -> /Users/username/Music/iTunes

rsync -avh --progress /Volumes/MusicMaster/iTunes.files/* /Volumes/Music/iTunes
rsync -avh --progress /Volumes/MusicMaster/iTunes.conf/* $HOME/Music/iTunes

Musicという名前でSDカードを初期化しておいて,USBメモリはMusicMasterという名前で初期化してる.だいぶ素朴.