D-7 <altijd in beweging>

Day to day life of a Perl/Go/C/C++/whatever hacker. May include anything from tech, food, and family.

カテゴリ:Go

色々Goで書いてみてやはりガードオブジェクトが欲しい、と思うことがよくある。一時的に何かの値を有効にしたり、かならず解放・リセット処理を行いたい場合のイディオムだ。これでいいのかわからないのだけど、とりあえず現時点で自分がやっていることをメモしておく。

Goにはdeferという仕組みがあって、関数の最後に必ずこれを実行してくれる。だがこれをオブジェクト構造体 につける方法はない。もちろん、構造体に対する関数呼び出しをdeferすることで同じ事ができるんだけど、そうするとどの関数を呼び出すのかをいちいち覚えていないといけない。一応runtime.SetFinalizer()というものはあるんだけど、これはいつ処理が走るのかわからないし、複数のオブジェクトやリソースが絡んでいる時にどの順番で処理が走るかもわからない。

ということはやはりdefer() を駆使するべきだろう。ということで自分は以下のような方法を使うことで今のところ落ち着いている。

func GuardLike () func() {
... 色々処理 ...
return func() {
... 解放処理 ...
}
}

func main() {
closer := GuardLike()
defer closer()
}  
これであればGuardLike()の中でどんな処理が行われていようと呼び出し側で気にすることなく関数をdeferから呼び出す、ということだけ覚えておけばよい。これでいいのかわからないので「こっちのほうがいいよ」という意見があれば教えて欲しい。

ちなみにPerl等のeval { ... }に相当するものを書きたい時も自分は関数でラップした上でdefer + recoverを使うなどしているので、goは結構このdeferの使い方をうまくすることで色々楽ができそうな気がする。

あとトランザクションのロールバックのような処理はなんか明示的に書いたほうがいいのかと思って今は明示的にTxnRollback()というラッパー関数を呼び出すようにしている。まだこのあたり流儀を統一できてない。
    このエントリーをはてなブックマークに追加 mixiチェック Share on Tumblr

まだ道半ばなんだけど、GroongaをGo+Perlなフロントエンドから使うシステムを動かしはじめている。

今回はとりあえず現時点での状態をざっくり書き出してみる。まだ本番化はしてないが、とりあえず本番環境からデータの挿入・削除ができるところまでつなぎ込みはした。最終的に全面的に本番化したらまたまとめ直します。

まずデータを突っ込む部分は慣れもあるのでより素早くデータの整形をしたりテーブルスキーマとかを変更したりするためにPerlでワーカーをさくさくっと書いた。ワーカーはQ4Mでデータを受け取り、データを整形してMroonga経由でデータを挿入。仕組みができたところでGroongaが我々が求めているデータ量をハンドリングできるかどうか検証するために全力で平均1行4KB~10KBくらいのデータをmroonga経由で○億件挿入してみた。この挿入処理中様々な地雷を踏んだので、kazeburoさんの助言などを元に挿入先のDBとテーブルをshardingしたりしてそれらを回避。

データは二つのよさげな検証用マシンにそれぞれ5000万件のところまではO(log(N))的な感じで挿入できていたが、それ以降は線形。これを書いている時点で1日約600万件の挿入ができている。5000万件の時点で関連grnファイルを全てあわせるとそれぞれのマシンに360GBずつそのあとはだいたい1日100GBずつくらい増えている。

sharding部分が落ち着いてきたら次は既存のシステムへのつなぎ込み用HTTP APIを作成。ここを今回はGoで作成。Goの部分は 練習もあったので簡単な参照部分をまずストレートなHTTP APIハンドラとして実装してみた。ステートレスなのでstructとかは基本的に作らず、golang.orgのExampleをほぼそのまま。

参照部分でどれだけ性能が出るのかどきどきだったけど、全く問題なし。Goちゃん速い。

次にデータ挿入部分はすでに生成してあるPerl製ワーカーとのつなぎこみだけなんだけど、queueへの書き込み部分はDBへの接続と使用テーブル両方ともある程度ランダムにばらしているのでその部分は全部Goで書き直し。この時点でサーバー側でステートを持ち始める必要ができてきたので、ApiServer型を作ってHTTPハンドラはクロージャで登録するような形に変更。コード的にはこれが大分おおきな変更だったけど、手を動かせばいいだけで、別にはまるところはなかった。定期的にランダムに挿入先をリフレッシュするように書いたんだけど、ここもgoroutineを使ってきれいにできた。time.Ticker便利!シグナル処理部分で一瞬迷ったけど、Perl5のSafe Signalsと同じ仕組みをchannelで実装しているだけだとわかってしまえばあとは簡単だったので、ごにょごにょしたり、一旦握りつぶしたシグナルをsyscall.Kill()でまた流したりとか色々した。

というわけで良い感じになってきた。そのうちこのシステムも皆様が触る部分にお目見えできると思っております。
    このエントリーをはてなブックマークに追加 mixiチェック Share on Tumblr

もう一つわりとまともなプログラム書いてみて、Go 1.1について所感。あくまで個人的な感触です。
  • やっぱりC書いてる人向けな言語な気がひしひしとする。
  • 例外がないならないでわりと気にならなくなってきた
  • 最初すごい使うだろうと思っていたdeferを意外と使わない
  • http.Requestとかはまだ貧弱な気がする
  • string[from:to] は結構便利
  • murmur hashの古いバージョンが必要だったのでポートしてみたけど、Cコードをそのまま置き換えるだけで楽だった。
  • ""と``は視覚的にわかりにくいと思うんだけど、まぁ使い方わかったら重宝してる。
  • 意外とほとんど組み込みライブラリだけでできる
  • slice.push()とかできないのすごく気になる。slice = append(slice, ...) は視覚的にすごい無駄な気がしてしょうがないんだけど、コンパイル時に最適化されてるんだろうな。
とりあえずGoは気軽に書けるし、結構高速だし、嫌いじゃないぜ。
    このエントリーをはてなブックマークに追加 mixiチェック Share on Tumblr

このページのトップヘ