Shogo's Blog

Aug 23, 2015 - 1 minute read - perl

YAPC::Asia2015へ行ってきた

YAPC::Asia2015へ行ってきましました。 Blogを書くまでがYAPCらしいので、簡単に 今年の会場は東京ビッグサイトです。 ▼▼みたいになってるところの中にはじめて潜入してきました。 あの中って会議室なんですね。 去年は毎回立ち見ですごく大変だったけど、今年はかなり会場が広くなったおかけで、 大体席を確保できて楽にトークを聴けました。 しかし会場が東京ビッグサイトであっても、人気トークは立ち見になってしまうのがYAPCのすごいところ・・・。 それでも、前の人の頭でスライドが全く見えないみたいなことはなかったので、広い会場は便利です。 以下、今年見たトークです。 言語開発の現場 はてなブックマークのトピックページの裏側 技術ブログを書くことについて語るときに僕の語ること タイトルが9割 世界展開する大規模ウェブサービスのデプロイを支える技術 全サーバで一斉にgit pullするつらい話だった と、思ったら途中からstretcherの話になった HTTP/2時代のウェブサイト設計 CSSスプライトみたいなファイルを一つにまとめてリクエストを減らす技術はHTTP/2ではオワコンになる 何よりもデータ量を減らすことが大事 【sponsored contents】若手エンジニア達の生存戦略 Google Cloud Platformの謎テクノロジーを掘り下げる 朝寝坊して途中からの参加でした(=_=) Googleのコンテナ技術BorgやGoogleのネットワークについての話 我々はどのように冗長化を失敗したのか MySQLで2億件のシリアルデータと格闘したチューニングの話 データ分析基盤を支える技術 いろいろなツールの比較についてのお話でした なんか色々なオープンソースのソフトウェアを紹介していたけど、「自分で構築しようとするな」とのこと D言語みんな使ってね Parallelism, Concurrency, and Asynchrony in Perl 6 Perl6では並列・並行・非同期処理が簡単に書けるらしいので、その紹介 Promiseやawaitみたいな他の言語で取り入れられている概念がPerlでも使えるらしい 来年Perl6でドローンが飛んでいるのを期待してます Profiling & Optimizing in Go Goのプロファイリングと最適化のデモでした sync.Pool 存在は知っていたけど実際に使っているコード始めて見た気がする。bytes.Bufferの作成に使っていたんだけど、メモリアロケート程度なら同期コストの方が高いのでは〜って思っていた。 改めて見返してみるとPerlについての話がPerl6の並列・並行・非同期処理くらいしかないきがする。 (YAPCのPとは一体) 最後に、呪いを掛けられたのでMySQL5.7の罠についてリンクを貼っておきますね。 http://yoku0825.blogspot.jp/2015/08/yapcasia-mysql-57lt.html Passwordの有効期限のデフォルトがいつの間にか360日になるのは話題になってたのを知っていたけど、他にも罠満載でした。

Jun 21, 2015 - 1 minute read - go golang

go-webtailってのを書いた

Rubyで書かれたwebtailのGo移植を書いてみました。 go-webtail オリジナルのwebtailはRubyなので、Rubyistではない僕が使おうとするとまずRubyの実行環境からそろえないといけなくてつらい。 ワンバイナリでダウンロードするだけで使えるやつが欲しいなあと常々思っていたのでGolangです。 htmlやjavasctiptの部分もバイナリに含まれているので、インストールも簡単です。 引数無しで実行すると8080ポートで待ち受けて、標準入力から読み込んだ結果をWebsocketで読めるようにしてくれます。 go get github.com/shogo82148/go-webtail/cmd/webtail # インストール echo hogehoge | webtail ファイルもtailできます。 webtail hoge.log fuga.log それぞれ、http://localhost:8080/hoge.logとhttp://localhost:8080/fuga.logで見れるようになります。 mirageと一緒につかう mirageは待ち受けポートを複数設定できます。 (SEE ALSO Dockerで非エンジニアでも開発環境を上げ下げできる、mirageというツールを作りました) その一つをwebtailに割り当てて以下のようにDockerfileに書いておけば、非(サーバサイド)エンジニアでも開発環境のログが見れるようになります。 (見れても理解できるのか?って疑問もあるけど、まあ、全く見れないよりは・・・) ADD webtail / CMD ./docker_run.sh 2>&1 | /webtail --prefix webtail # ブラウザで見れる代わりにdocker logsで見れなくなるのでこっちのほうがいいかも CMD ./docker_run.sh 2>&1 | tee hoge.log | /webtail --prefix webtail 残念ながらwebsocket対応はしていないので、websoket対応にしたmirageが必要です。 httputil.NewSingleHostReverseProxy互換のrproxyってのを使ったら簡単にwebsocket対応ができて素晴らしいですね。 (mirage自身に手を加える必要があるなら、mirageにこういう機能をつけるべきだったのでは説はある)

Jun 20, 2015 - 1 minute read - 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 - 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文の扱いが変わったりしているので、その部分だけ書き換えが必要です。 import MeCab tagger = MeCab.

May 21, 2015 - 1 minute read - 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 - 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.Result) error { log.

May 9, 2015 - 2 minute read - 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.com/shogo82148/txmanager" ) func Example(db *sql.

May 3, 2015 - 3 minute read - 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 - go golang

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

ちょっとGIFアニメを作りたくなって、最近Go触ってるしGoでやってみよう!とやってみたメモ。 ImageMagikでいいじゃん説もあるけど、最終的にツールとして配布したいなってことでGoです。 主に減色まわりについて。 2021-12-07修正 昨今のアレコレ(LOSING LENA)の関係で記事中の Lenna さんの画像をマンドリルに置き換えました。 何はともあれ実装してみる 以前、「ターミナル操作の記録(ttyrec)からGIFアニメを生成するツールを作った」という記事を見たので、 これを参考に実装してみる。 package main import ( "image" "image/color/palette" "image/gif" _ "image/png" "os" ) func main() { reader, err := os.Open("Mandrill.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.

Apr 25, 2015 - 2 minute read - 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番目に移動する確率を $P_n(i, j)$ と定義します(golangのコードにあわせて0-originで考えます)。 完全にランダムにシャッフルされていれば、 元の数列のどの要素も0からn-1の範囲に一様分布するはずです。 つまり、以下の式がなりたてば「シャッフルされている」と言えそうです。 $$ P_n(i, j) = \frac{1}{n} (i, j = 0, \dots, n - 1) $$ n=1の場合 n=1の場合は、必ず0番目と0番目の入れ替え(つまり順番変わらない)になります。 上で定義した確率を計算すると$P_1(0, 0) = 1/1$となるので、シャッフルされていると言えます。