Shogo's Blog

Jun 20, 2015 - 1 minute read - Comments - perl

Test::mysqld::Multiというモジュールを書いてみた

Test::mysqldのインスタンスを一度に大量に作りたい人向けに Test::mysqld::Multiというモジュールを書いてみました。 2016/12/22追記: Test::mysqld::MultiはTest::mysqld 0.20 の一部として取り込まれました (p5-Test::mysqld#13)。 APIは少し変わっているので、詳しくはPODを参照してください。 合わせてApp::Prove::Plugin::MySQLPool 0.06 より、 本記事で紹介した高速化が利用できます。 背景 先日Jenkins EC2 Plugin で Spot Instance を使ってテストを回すというのを、 tkuchikiさんにお願いして僕の関わっているプロジェクトでやっていただきました。 CPUのたくさん載ったインスタンスを安く使えるようになったので、 8並列で動いてたテストを24並列で動かせるようになりました。やった3倍速だ!!! 9分程かかってたテストが7分で終わるようになりました!!! あれ・・・思ったほど早くなってない・・・。 ログを眺めているとproveコマンドが立ち上がってから、実際にテストが走り始めるまで数分の時間がありました。 App::Prove::Plugin::MySQLPoolを使っているのですが、 ここで時間がかかっているようです。 App::Prove::Plugin::MySQLPoolはテストの並列度分だけMySQLのインスタンスを立ち上げますが、 一個インスタンスを立ち上げたら、それにアクセスできるようになるまでずっと待っているようです。 MySQLの起動に5秒かかるとして24並列で動かしたら2分かかるわけで無視できない長さになります。 作ったもの n個一度に立ち上げて全部にアクセスできるまで待つ実装にすれば速くなるのでは!ってことでTest::mysqld::Multiというのを書いて、 App::Prove::Plugin::MySQLPoolからそれを使うようにしました。 とりあえずtest-mysql-multiブランチにコミットしてあります。 App::Prove::Plugin::MySQLPoolに取り込んでもらうか別のモジュールとして分離するか、後々のことは未定。 今のプロジェクトで使ってみてちょっとの間様子見してみます。 7分かかってたテストが5分程度で終わるようになったので、効果はあるようです。 ちなみに、並列度が24と半端なのはそれ以上並列度を上げても速くならなかったため。 32コアあるマシンなんだけど使い切れてません。 どこにボトルネックがあるんだろうな・・・。 まとめ プロセス一覧にmysqldが24個並ぶの楽しい

Jun 2, 2015 - 1 minute read - Comments - mecab python python3

MeCabをPython3から使う(中間報告)

先日このようなツイートを見かけて、 「Python3になってGCの挙動変わったのかな?」と疑問に思ったので調査してみました。 MeCabをPythonから使う注意点とか - Shogo's Blog http://t.co/vJnOqZfUd7 @shogo82148さんから python3だと変数に代入しなくても動くのだけど2.xでは留意しないといけない — NOKUBI Takatsugu野首貴嗣 (@knok) 2015年6月1日 Python3へのMeCabインストール 手元のPython3.4.3にMeCab Bindingをインストールします。 MeCabの公式(Google Codeサービス停止にともないgithub pageへ移行している模様)から落とせる Python BindingはPython2.x向けのため、setup.pyがそのままでは動きません。 Python3.xでは非互換な文法の変更が入ったので以下のように書き換える必要があります。 diff --git a/setup.py.org b/setup.py index 4486cbb..657945a 100644 --- a/setup.py.org +++ b/setup.py @@ -7,7 +7,7 @@ def cmd1(str): return os.popen(str).readlines()[0][:-1] def cmd2(str): - return string.split (cmd1(str)) + return cmd1(str).split() setup(name = "mecab-python", version = cmd1("mecab-config --version"), あとは python setup.py install で入ります。 動かしてみる 以前書いた「MeCabをPythonから使う注意点とか」を見返しながら、 GCされて上手く動かない例 をPython3.4.3で動かしてみます。 文字列の扱いが変わったり、print文の扱いが変わったりしているので、その部分だけ書き換えが必要です。

May 21, 2015 - 1 minute read - Comments - git

各ブランチの最後にコミットした人を知る

ブランチが大量にあるので整理したい、けど大人数で開発しているから誰がどのブランチいじってるか分からない、 ということがあったので、出し方のメモ。 githubのbranch一覧も見ればいいじゃん!っていう意見もあると思うんだけど、 「自分のbranch一覧」は見れるんですが「特定のだれかのbranch一覧」が見れない・・・。 git-for-each-refを使うと各ブランチに対していろいろ操作できるようです。 各ブランチの最後にコミットした人一覧を出すには以下のコマンド。 git for-each-ref --format='%(authordate:short) %(authorname) %(refname)' --sort=-committerdate refs/remotes/origin/ formatは自由にいじれるのでいろいろ遊べます。 例えば、ブランチをたくさん抱え込んでいる人の一覧を表示する例。 git for-each-ref --format="%(authorname)" refs/remotes/origin/ | sort | uniq -c | sort -nr 参考 git-for-each-ref - Output information on each ref リモートブランチも含め更新日時が新しい順番にソートする ブランチ一覧を更新時刻つきで表示したい場合、gitのfor-each-refが使える。

May 13, 2015 - 1 minute read - Comments - go golang

Go言語でSQLのトレースをする

ぴっぴ先輩が「Go言語で発行したクエリを確認したい」って言ってて、 「MySQL使っているならGeneral Logを吐けばよいのでは?」と返したんだけども、 もっと汎用的な方法はないものかと考えてみました。 Golangの database/sql はどんなDBでも対応できるよう、ドライバを自由に入れ替えることができます。 ドライバは単にdatabase/sql/driverにあるインターフェースを満たしている何かなので、 ユーザが自由に作ることができるし、interfaceを経由して直接呼び出すことも可能です。 この仕組を使って、別のドライバにそのまま渡すプロキシを作れば、ログを吐けるのでは?ということでやってみました。 go-sql-proxy 使い方 まず最初にgo-sql-proxyをドライバとして登録します。 hooks := &proxy.Hooks{ // Hook functions here(Open, Exec, Query, etc.) } sql.Register("new-proxy-name", proxy.NewProxy(&another.Driver{}, hooks)) あとは登録したドライバと使って新しいDBハンドラを開くだけです。 db, err := sql.Open("new-proxy-name", dataSourceName) このハンドラを使ってクエリ実行を行うと、Hooksで登録した関数が呼び出されます。 元のドライバを直接使った場合と同じように振る舞うので、既存のコードを一切変えること無くHookを差し込めて便利! トレーサの例 簡単なトレーサを書いてみるとこんな感じ。 発行したSQLのクエリをログに吐き出します。 package proxy import ( "database/sql" "database/sql/driver" "log" "github.com/mattn/go-sqlite3" "github.com/shogo82148/txmanager" ) func main() { sql.Register("sqlite3-proxy", NewProxy(&sqlite3.SQLiteDriver{}, &Hooks{ Open: func(conn *Conn) error { log.Println("Open") return nil }, Exec: func(stmt *Stmt, args []driver.Value, result driver.

May 9, 2015 - 2 minute read - Comments - go golang

Goのトランザクションマネージャ作った

Golangのdatabase/sqlはBeginとCommitでトランザクションの制御を行うことができます。 クエリの実行が確実に成功するのであれば難しくは無いのですが、 トランザクション内でエラーが発生場合、確実にトランザクションを終了させるのは少し面倒です。 また、ネストができないので、「トランザクションの中から呼び出しても外から呼び出しても、関数の中はトランザクション内」みたいなことができません。 PerlにはDBIx-TransactionManagerというものがあるのですが、 このGolang版が欲しくなったので作ってみました。 txmanager 簡単な使い方 sql.DB をラップした txmanager.DB を使います。 Begin, Commit する代わりに TxBegin, TxCommit を使ってトランザクションを開始・終了すると txmanagerの管理下になります。 確実にトランザクションが終了させるために、トランザクションを開始したらdefer tx.TxFinish()を忘れないように。 import ( "database/sql" "github.com/shogo82148/txmanager" ) func Example(db *sql.DB) { dbm := txmanager.NewDB(db) // トランザクション開始 tx, _ := dbm.TxBegin() defer tx.TxFinish() // INSERTはトランザクションの中で実行される _, err := tx.Exec("INSERT INTO t1 (id) VALUES(1)") if err != nil { tx.TxRollback() } tx.TxCommit() } 実際にはこれに加えてエラー処理も必要です。 txmanager.Do を使うと、トランザクションの開始処理・終了をtxmangerがやってくれるので少し楽になります。 import ( "database/sql" "github.

May 3, 2015 - 3 minute read - Comments - go golang

Go言語でGraceful Restartをする

とあるHTTPサーバをGolangで立てようって話になったんだけど、 止まると困るので無停止でサーバ再起動をしたい。 PerlにはServer::Starterという有名モジュールがあるんだけど、 Golangはどうなってるの?ってことで調べてみました。 2017-01-22追記: Go1.8以降でGraceful Shutdownがbuild-inになるので、この記事で紹介したライブラリは不要となりました。 詳しくはGo1.8のGraceful Shutdownとgo-gracedownの対応を参照。 gracefulじゃないバージョン Golangの標準ライブラリを使ってHTTPサーバを立ててみる例。 レスポンスが一瞬で終わってしまうとよくわからないので、sleepするhandlerを追加しておきます。 package main import ( "fmt" "log" "net/http" "os" "time" ) var now = time.Now() func main() { log.Printf("start pid %d\n", os.Getpid()) s := &http.Server{Addr: ":8080", Handler: newHandler()} s.ListenAndServe() } // https://github.com/facebookgo/grace/blob/master/gracedemo/demo.go から一部拝借 func newHandler() http.Handler { mux := http.NewServeMux() mux.HandleFunc("/sleep/", func(w http.ResponseWriter, r *http.Request) { duration, err := time.ParseDuration(r.FormValue("duration")) if err != nil { http.Error(w, err.Error(), 400) return } time.

Apr 25, 2015 - 2 minute read - Comments - go golang

Go言語で画像の減色を行う

ちょっとGIFアニメを作りたくなって、最近Go触ってるしGoでやってみよう!とやってみたメモ。 ImageMagikでいいじゃん説もあるけど、最終的にツールとして配布したいなってことでGoです。 主に減色まわりについて。 何はともあれ実装してみる 以前、「ターミナル操作の記録(ttyrec)からGIFアニメを生成するツールを作った」という記事を見たので、 これを参考に実装してみる。 package main import ( "image" "image/color/palette" "image/gif" _ "image/png" "os" ) func main() { reader, err := os.Open("Lenna.png") if err != nil { return } defer reader.Close() img, _, err := image.Decode(reader) if err != nil { return } paletted := image.NewPaletted(img.Bounds(), palette.WebSafe) for y := img.Bounds().Min.Y; y < img.Bounds().Max.Y; y++ { for x := img.Bounds().Min.X; x < img.Bounds().Max.X; x++ { paletted.Set(x, y, img.At(x, y)) } } f, _ := os.

Apr 25, 2015 - 3 minute read - Comments - go golang

Go言語でshuffleする話

Fisher-Yates shuffleを使ってシャッフルライブラリ作ってみました。 https://github.com/shogo82148/go-shuffle 標準ライブラリのsortと似たような感じで使えます。 デフォルトでintとfloat64とstringのシャッフルに対応していて、 他の型をシャッフルしたい場合はインターフェースを実装してね、って感じです。 実装が簡単なので、インターフェース定義する手間とシャッフルのアルゴリズム自前で書く手間ほとんど一緒ではという気もするけど、 まあライブラリ作成の練習ってことで。 で、ここからが本題。 Fisher-Yates shuffleの名前は以前から知ってたけど、 この前某プロジェクトで以下のようなshuffleの実装を発見。 package main import "math/rand" func shuffle(a []int) { for i := range a { j := rand.Intn(i + 1) a[i], a[j] = a[j], a[i] } } Fisher-Yates shuffleと似ているけど、なにかが違う。 ちゃんとシャッフルされているのか気になったので検証してみました。 検証 n個の数列をシャッフルすることを考えます。 シャッフルの後i番目の要素がj番目に移動する確率を {% m %}P_n(i, j){% em %} と定義します(golangのコードにあわせて0-originで考えます)。 完全にランダムにシャッフルされていれば、 元の数列のどの要素も0からn-1の範囲に一様分布するはずです。 つまり、以下の式がなりたてば「シャッフルされている」と言えそうです。 {% math %} P_n(i, j) = \frac{1}{n} (i, j = 0, \dots, n - 1) {% endmath %}

Apr 19, 2015 - 1 minute read - Comments - isucon

社内ISUCONにチームぽわわ3.5で参加しました

木曜日の社内ISUCONにチームぽわわ3.5として参加してきました。 (今年のISUCON本番に4にアップデート予定) さきに結果だけ書いておくと、 1位はfujiwaraさんとacidlemonさんのチーム、 2位はチームぽわわ3.5、 3位はぴっぴ先輩率いるチーム例の青い紐でした。 オムライスと紐を倒したので僕は満足です。 簡単にやったことを書いておきます。 課題内容 Twitterみたいな短文投稿サイトです。 トップページにアクセスすると全ユーザの発言最新100件がみれて、 ログインすると発言したり自分の投稿履歴を確認したりできます。 僕が新卒で入ってきたときはPerlでしたが、今年の参考実装はGolang製です。 (Rubyもあったらしいけど使った人いたのかな) やったこと 僕自身は、相方になったたいがさんに「こんなことしてみては〜」と言ってみる係をやってました。 具体的な対応としては以下の通りです。 nginxにレスポンス吐かせる Nginxのレスポンスタイムをパーセンタイル値で計測するMunin plugin とかを参考にしてもらって、レスポンスタイムを吐くようにしてもらいました。 ログをテキトウスクリプトで集計したとろこ、トップページの全ユーザの発言最新100件みれるページが重いみたい。 高速化の第一ターゲットをトップページにしぼりました。 MySQLにSlowQuery吐かせる トップページが重いっぽいというのはわかったものの、 どのクエリが重いかまでは分からない(もちろんコード読んでたので検討はついてたけど)ので、 処理に0.1秒以上かかっているクエリを吐くようにしました。 インデックスの追加 既存のコードに触れずにお手軽ってことで、まずはDBにインデックスを張るところから。 workload10で、99583から101033にスコアアップ! まあ、他のボトルネックを潰していない段階だとこんなもんでしょうね・・・。 ループクエリ・無駄クエリの削除 明らかに無駄クエリっぽいところがあったのでそこを修正しました。 投稿100件取得したあとに、100回ユーザ名の取得処理をしている JOINを使って書き換えました 実行計画が狂って逆に遅くなるという事態に陥ったので、IGNORE INDEXとかして頑張った ユーザの投稿を全取得してるのに、最新1件の情報しか使ってないところ LIMITをつけて制限 全投稿をCOUNTしているところ せっかくGolang使ってるんだから楽しようと、グローバル変数に突っ込んでcount++してみた 「グローバル変数に突っ込んでみた」対策みたいに、下手にアプリサーバで情報を保持すると DBとアプリサーバに差ができてしまうので、実運用では避けるべきテクニックですね。 あとになって考えると、ベンチ回す前にアプリサーバの再起動忘れてたのにベンチ通ってたので、 投稿数数えなくてもよかったのでは・・・。 nginxによる静的ファイルの配信 cssとかjsをGolangでかえしていたので、nginxで返すようにしました。 これで724338から802905(workload:100)にScoreアップ! 画像の縮小 Twitterらしく投稿には100x100程度のサイズのアイコンが表示されるんですが、 元画像が1000x1000程度だったので縮小しました。 ただ、ベンチが画像にアクセスしにこないので、まったくの効果なし。 最終計測では結局元画像に戻しました。 実運用では確かに効果があると思うんですが、まずはログを見て判断しろという教訓ですね。 まとめ あとはworkloadの調整とかやって最終スコアは935519でした。 2位にはなったものの、インデックス追加とかループクエリの削除とか最低限のことが何とか出来たって感じです。 もっと精進します。 tech kayac へのポストまだかな〜

Apr 9, 2015 - 1 minute read - Comments - perl

Perlで文字列の出現回数を調べる

Perlで特定の文字列の出現回数を調べたくなって、調べてみたメモ。 ググるとすぐに見つかった。 perlで指定文字列の出現回数を取得する(正規表現) 指定文字列の出現回数は正規表現を使って $count++ while($str =~ m/$pattern/g); もしくは $count = (() = $str =~ m/$pattern/g); が、一瞬何をやっているのか把握できない・・・。 こういう意味なのかなーって予想はしてみたけど、あってるか一応調査。 whileを使った方法 //g をスカラーコンテキストの中でマッチさせると、 前回マッチした場所を覚えておいてくれて、次のマッチでその場所から検索を再開してくれるらしい。 (Using regular expressions in Perl - perlretut) マッチした場所は pos で取得可能。 my $str = "hoge fuga foo bar"; while ($str =~ m/[a-z]+/g) { say pos $str; } whileを後置にして、ループの回数を数えるようにすれば、最初の方法になる。 ループを使わない方法 これが一番謎だった。 //g をリストコンテキストで評価すると、マッチした文字列がリストになって帰ってくるらしい。 (Quote-Like Operators - perlop) 複数の変数に一括して代入するときに ($foo, $bar) = (1, 2) みたいな書き方をするけど、 () = ... の部分はこれの代入先の変数が一個もないケース。 要するに「リストコンテキストで評価してね」という意味のイディオムみたい。