その手の平は尻もつかめるさ

ギジュツ的な事をメーンで書く予定です

Jesque を頑健に使うために RobustWorkerPool というのを書いた

8/16 (日) に京都でハッカソンが催されるとの事だったので遊びに行って,表題のものを作ってきました (実際には大部分は予め作っていた).

Jesque は Resque の Java 実装版で,今やっているやつではこれを使った JobQueue - Worker なシステムの実装を進めています.
しかし Jesque Core の Worker Pooling の実装は調べてみると微妙な感じで,具体的にどう微妙かと言うと「Worker を Pooling する (つまり Worker を任意個生成する)」というところまでは面倒を見てくれるのですが,その Worker 群に対して死活監視等のマネジメントの一切をしないという問題があり,これは「不慮の事故で Worker が死んでしまった時 (Worker が poll している時に例外が上げられた時とか) にその Worker を生き返らせる」などということを全くしてくれない事を意味します.それでは不便なので,その部分を解決する実装を書いたというのが今回のハッカソンの成果となります.

これがどんなものかというと,

  • Worker を複数個生成して pooling する
  • Worker が停止したら (この時は WORKER_STOP というイベントが発火されるのでそれを hook する),
    • その Worker を pool から除去して
    • もしアクティブな総 Worker 数が最大 Worker 数を下回っていたら停止した Worker の情報を元にして新しい Worker を生成し,pool に加える
    • もしアクティブな総 Worker 数が最大 Worker 数を上回っていたら Pool から Worker を除去する (この時は Worker を Graceful に 終了させる)

- Worker が何らかのエラーを吐いてきたら (この時は WORKER_ERROR というイベントが発火されるのでそれを hook する) Graceful に当該 Worker を停止させる (停止させると WORKER_STOP が発火するので,Worker 数の調整ロジックに入る)

という動きをするように既存の Worker Pooling 機構を改良したものになります.この辺の実装は Parallel::Prefork を参考にして書いていきました.非常に参考になる……

で,実際にこれを使ってみると Worker がいきなり死んでしまっても Worker Pooling の機構自体が Worker を生き返してくれるので一定の安心感があって良い感じがしています.良い感じがしたので Maven Central にも上げるということをしました (これ,Jesque のコアでサポートして欲しいのだけれど……)


以下はハッカソンの成果発表時の資料になります.この時点では完全に酔っ払っていた事を表明します.

www.slideshare.net


ところでハッカソン会場にははてなインターンの学生が大勢いて,僕も2年前はその一員だったのだな,そういえば JobQueue についてちゃんと学んだのははてなインターンが初めてだったな,という記憶が続々フラッシュバックしてきて,そうした場で JobQueue に関連するコードを書くのはなにやら感慨深いものがありました.そうした気持ちだったので上記のスライドの前半は Resque (Jesque) 入門のような内容になっています.


ご利用下さい.

ブログ等でGitHub等に置いてあるソースコードをURLで示すときはリビジョンで指定したほうが良い

まあ当たり前の話なんですが,ブログなんかで GitHub (とかそれに準ずるサービス) に置いてあるソースコードをURLで示す時に

https://github.com/git/git/blob/master/connect.c#L105

みたいな風に指定してしまうと,master 上の実装が変わった時にトンチンカンなところを指し示してしまい,意味不明な感じになって涙ということがままあります.

https://github.com/git/git/blob/7a4f891329527826e6f1c80be2b77cfbfee06190/connect.c#L105

のように指定したほうが良い (ブログのようなストック型の情報の場合は特に) と思った次第.頼む!!!!!!



得情報


github.com

go-setlock - Go port of setlock

setlock is a command line tool which is in daemontools. That tool is known as powerful and useful.


Then I implemented setlock command by golang, called go-setlock.

go-setlock has functions which are equivalent to the original setlock. And go-setlock has following pros;

The former is useful for environments which cannot install daemontools, minimalists and etc.
The latter is pretty powerful. The original setlock is not available on Windows however go-setlock can use on there.


Usage is matched to the original setlock. Please refer README.md and manual of the original setlock.
Enjoy!

go-setlock - Go port of setlock

[2015/08/10 12:47 追記]なんか一部のOS Xで動かないので調査中です……
[2015/08/11 10:15 追記]v1.1.0で直っています


setlockdaemontools に同梱されているコマンドラインツールで,非常に便利かつ強力なツールであることが知られています.この件については以下の記事などが参考になります.


そしてこの度,その setlock を golang で移植した go-setlock というものをこさえました.

go-setlock は setlock と同等の機能を持っており,かつ以下の様なメリットが挙げられます.

  • daemontools から独立しているので,setlock のみを使いたいときにわざわざ daemontools ごとインストールする必要がない
  • Windows でも使える!!

前者は諸般の事情により daemontools をインストール出来ない環境でお暮らしの方 *1 や、ミニマリストの方にとって役立つでしょう.
後者はなかなか強力です.オリジナルの setlock は Windows 上で使えなかったわけですが,go-setlock はテクノロジにより Windows でも動作するようにしたので便利です (試しに Windows のメディアサーバ上で動画のエンコードを排他的に行う,みたいな事をやってみたんですが便利).


使い方はオリジナルの setlock と揃えています.README.mdmanual of the original setlock をご参照ください.
ご利用くださいませ.

*1:そんな環境があるのか

Windows で flock の LOCK_EX + LOCK_NB のような感じでファイルロックする

"windows LOCK_NB" とかでぐぐると無限に PHP の情報がヒットしてなかなか所望の情報に辿りつけなかったのだけれど,どうやら LockFileEx を使えば良いらしい.
LockFileEx function (Windows)


LOCKFILE_EXCLUSIVE_LOCK (0x00000002) を dwFlags に指定してやるとファイルを排他ロックすることができ (指定しない場合は共有ロック),LOCKFILE_FAIL_IMMEDIATELY (0x00000001) を指定するとロックを取得できなかったら即座に失敗を返してくるようになる (指定しない場合はロックが得られるまで待つ).つまり,これらは flock の LOCK_EX と LOCK_NB に対応していると言える.
つまり,以下のように使うと flock の LOCK_EX | LOCK_NB 相当の事ができる (コードはここから持ってきた:
Locking and Unlocking Byte Ranges in Files (Windows)).

fSuccess = LockFileEx(hFile,         // exclusive access, 
                      LOCKFILE_EXCLUSIVE_LOCK | 
                      LOCKFILE_FAIL_IMMEDIATELY,
                      0,             // reserved, must be zero
                      TESTSTRLEN,    // number of bytes to lock
                      0,
                      &sOverlapped); // contains the file offset


便利便利.

OS X で Jenkins 公式の docker イメージを起動する

Jenkins の plugin を開発するにあたってローカルに Jenkins を立てる必要が出てきて,さてどうしたものかと思っていたら Jenkins が公式で docker イメージを出しているのでこれを使えば良いじゃん! ってことで使うことにしました.以下メモ.

前提

VirtualBox がマシンに入っている.

docker と boot2docker 入れる
$ brew install docker boot2docker
boot2docker を立ち上げる
$ boot2docker download
$ boot2docker init
$ boot2docker up
VirtualBox の App から boot2docker の仮想マシンの Port Forwarding の設定をする

スクリーンショット参照

f:id:moznion:20150804000830p:plain

Jenkins の docker イメージを立ち上げる
$ git clone git@github.com:jenkinsci/docker.git
$ pushd docker
$ docker run -p 8080:8080 -p 50000:50000 jenkins

そしてブラウザで localhost:8080 にアクセス

f:id:moznion:20150804000919p:plain

動いた,簡単!
とは言えローカルのプラグイン開発ならまだしも,これを実戦で運用していくとなるとまた話は変わってくるのでしょうね.大変そう……

その他

docker run 時に "Error response from daemon: client and server don't have same version (client : 1.19, server: 1.15)" みたいなエラーが出ることがありますが,こういう時は boot2docker の iso を新しくすれば良いので,

$ boot2docker stop
$ boot2docker delete
$ boot2docker download
$ boot2docker init
$ boot2docker up

としてやると OK.

Groovyで複数のコマンドを一気に実行したいんですけど〜って時

&& でつなげて複数のコマンドを実行したいみたいなことがままあって,まあ gradle とか書いてれば頻繁にあると思うんですけど,

'git add -u . && git ci -m "Yeah"'.execute()

みたいにやってみるもののこれは上手くいかない.
そこでどうするかというと

['sh', '-c',  'git add -u . && git ci -m "Yeah"'].execute()

という風にしてやればよろしい.sh にコマンドをグワッと押し込んで実行させるという男気! Windows? 適宜頑張りましょう.
便利ですね,以上です.