読者です 読者をやめる 読者になる 読者になる

isuconで惨敗しました

isucon

isuconでチームフリー素材として参加しました。54,868点で8位でした

今回は、開発環境の構築で色々とてこずりました。

DBがpostgresqlだったので、手元で構築しようとしていたときに、brew updateしてしまい、postgresqlのdatabaseのバージョンが一致しないので起動できないという状態になってしまいました。それで色々やっていると、手元のdatabaseを壊してしまってちょっと復旧は諦め、3台のうち1台を開発サーバーにしました。

deployできるようにしようとしていたのですが、手元からsshはとおるのですが、サーバー間でsshがつながらない状態だったので、iptablesなどを疑ったのですが、わからず、仕方なく、手元へbinaryをscpして、remoteへscpしなおすshell scriptを書いてなんとかしました。 sshがつながらなかったのは、懇親会で聞いたところ、hosts.denyの設定がされていたから、だったらしいです。

ようやく色々できるようになったのが、13:30ごろでだいぶ出遅れてしまいました。

serviceを取得する処理がN+1クエリだったので、INを使うように書き換えました。(追加がないのはその時点でわかっていたので、他のチームのようにハードコーディングするべきだった)

あとは他の人へ指示を出しつつ、curlでどのAPIがどのぐらいキャッシュできそうか、などを確認していました。

tenkiは最後まで悩んでいたのですが、curlを連打していると同じ結果が返ってくるので数秒ならいけそうと試してみたらtestが通ったので、徐々にあげてみて、2秒で落ちつきました。

グローバルIPしか持っていなかったので、どうやって複数台で分散させようか悩んだのですが、前回のようにログイン後のRedirect時に別のIPへRedirectさせたらどうか、ということでやってみましたが、test部分でfailしていたので、諦めてnginxでグローバルIPを直でproxyするのを試したりしました。

ただ、DB部分をグローバルIPでアクセスさせるという発想はなかったので、結局諦めて1台でがんばることにしました。

色々知識不足などで効率的に動けなかったので、復習して次回に備えようと思います。

2行書きかえるだけでSQLをログに出力できるようになるgo-sql-tracerを作りました

go

isucon予選中に、SQLを出力したくなったのだけど、ふとgithub.com/shogo82148/go-sql-proxyを使ってみようとしてとりあえず簡単に使う方法が書いてなくて少し使い方を調べるのに苦労したので、isuconで時間のロスなく使えるようにそのwrapperを書きました。

package main

  import (
    database/sql
    "github.com/go-sql-driver/mysql" # load driver before load go-sql-tracer
    _ "github.com/walf443/go-sql-tracer"
  )

  func main() {
    db, err := sql.Open('mysql:trace', dsn)
  }

ポイントは、

  • 元々使っていたドライバ名に":trace"とつけること
  • go-sql-driver/mysqlをimportしたあとに、importすること

です。

今database/sqlに登録されているドライバ一覧から、go-sql-tracerのinit内で、':trace'をつけてドライバを登録しているので使いたいドライバの後でimportする必要があります。

go-sql-proxyはカスタマイズも柔軟にでき、非常に素晴らしいライブラリですが、カスタマイズしなくてもいいのでとりあえずSQLのログみたい、というときは非常に便利かと思います。

github.com

じわじわチャットへ通知する

go

なんかのバッチを実行していて、それが出力するログをじわじわチャットへ通知したい。(そのバッチはそれなりに時間がかかるものとする)

何も考慮しなければ、たとえばslackであれば、

# #!/bin/bash
# notify_chat.sh

exec tee >(while read line; do curl -s --data '$line' $SLACK_ENDPOINT -o/dev/null; done

みたいにやれば、

$ ruby batch.rb | notify_chat.sh

で毎行slackさんへ通知してくれる。


このやり方だと、場所によってはAPIをどばっと叩いてしまって負荷をかけてしまったりするので、1秒間の出力はまとめてわたせるようにするコマンドthrottleというものを書いてみた。

  $ ruby batch.rb | tee >(throttle -interval 1s "curl -s --data '%%DATA%%' $SLACK_ENDPOINT -o /dev/null")

みたいにやると、1秒間の出力はまとめてcurlコマンドへ渡してくれる。1秒間の出力は、%%DATA%%の部分が置きかえられて実行される

$ go install github.com/walf443/throttle

でインストールできる

ソースは、

github.com

Chrome拡張でCocoaPodsのPodfile.lockのdiffからも各ライブラリの変更へ飛べるようになった

昨日だしたやつが、CocoaPodsのPodfile.lockのdiffからもclickできるようになりました。

また、Pull Requestのページのdiffで発動しなかった問題を修正しました。
権限まわりを変更している関係で、既にインストールした人はv0.0.2にアップグレードされた人は、無効になっていることがあるので注意してください。

Github Library Changes - Chrome ウェブストア

f:id:walf443:20150803075554p:plain

github.com

関連

walf443.hatenablog.com

githubのGemfile.lockやpackage.jsonのdiffからそれぞれのライブラリのdiffへ飛べるChrome拡張を作った

ChangeLogのファイルはプロジェクトにあったりなかったりなので、githubリポジトリのタグ間のdiffへクリックでいけるようにしてみた。
これでbundle updateとかしたときにざっくりどういう更新があったのか確認ができる。

現在は、Gemfile.lockのdiffと、package.jsonのdiffに対応している。

Github Library Changes - Chrome ウェブストア

f:id:walf443:20150803075554p:plain

github.com

rake db:migrateをRidgepoleで置き換える

rails

ちょっとRidgepole試してみようかなと思って使いはじめた。rails書いていると他の人がmigrationふつうにしようとしてしまったりするだろうから、rake db:migrateのデフォルトの動作を無効にして、ridgepoleを動作させたい。

lib/tasks/ridgepole.rakeとかで、

# overwrite db:migrate or db:dump
task 'db:migrate' => :environment do
  ENV['RAILS_ENV'] ||= "development"
  sh "bin/ridgepole -E#{ENV['RAILS_ENV']} -c config/database.yml --apply"
  sh "bin/ridgepole -E#{ENV['RAILS_ENV']} -c config/database.yml --export -o Schemafile"
end

task 'db:dump' => :environment do
  ENV['RAILS_ENV'] ||= "development"
  sh "bin/ridgepole -E#{ENV['RAILS_ENV']} -c config/database.yml --export -o Schemafile"
end

applyした直後でexportするべきかはどうかなーと思ったりしつつ、db:dumpいちいち打つのがダルいことが多いのかな。ということでやることにした。

Gemfileで、activerecordのedgeを指定する

bundler rails

先に結論をまとめると、こうすればよいらしい。

gem 'arel', git: 'https://github.com/rails/arel.git'
git 'https://github.com/rails/rails.git' do
  gem 'activerecord'
end


ライブラリで、特定のバージョン以上にbundlerが指定されていて、それを一時的にtravisなどで、HEADでテストを走らせたい、というときにどうするか、ということですが、railsの場合には、

rails/
-------rails.gemspec
-------activerecord/
-------------------activerecord.gemspec
-------actionpack/
-------------------actionpack.gemspec

のようなディレクトリ構成になっていて、directoryのtopにgemspecがある場合には、gitオプションで指定すればよいのですが、それ以下のディレクトリはどう指定したらいいんだろうか、と悩んでいたのでした。

test with active_record edge. · 3233386 · walf443/activerecord-mysql-unsigned · GitHub