八発白中

はてなブログに引越しました。

Common LispのResqueクローン「Lesque」を作りました

明けましておめでとうございます。今年もよろしくお願いします。

昨年の暮れは、引越しという大掃除を終わらせたこともあってさほど忙しくもなく、とはいえ一年を振り返る気にも到底ならなかったので前々から欲しかったCommon Lispライブラリを作っていました。

ひょっとしたら昨年中に間に合うかもしれない、と思っていたのですが残念ながら間に合わず。今日になってようやくまともに動くまでになったので「Lesque」と名づけてGitHubに公開しました。

Lesqueとは

Lesqueは処理(ジョブ)をバックグラウンドで実行するためのジョブキューイングシステムです。ジョブはRedisにJSON形式で記録され、別プロセスで動いているワーカーが溜まったジョブを捌いていきます。

たとえば、Webアプリケーションでツイート全削除のような時間がかかる処理を同期的にやると、処理が終わるまでユーザを待たせることになります。このツイート全削除の部分だけをジョブとして定義しておき、処理自体はバックグラウンドで実行できればユーザを長く待たせることなくページを表示できます。

PerlではTheSchewartzやQudoのようなMySQLをバックにしたジョブキューが使われていることが多いようですが、RubyPythonではRedisを使ったものがよく使われているようだったのでRedisをバックとしたシステムにしました。

実装はRubyResqueをほぼそのまま移植しています。そのためRedisに記録されたジョブの形式には互換性があります。

LとRの発音の違いにご注意ください。

使い方

まずはLesqueのコネクションを張ります。

(ql:quickload :lesque)

(defvar *lesque* (lesque:connect :host "localhost" :port 6379))

次にジョブを追加します。ジョブはただの関数です((クラスにすることもできます。lesque:jobを継承するクラスを作り、lesque:performを実装してください。))。

(defun deferred-job (&rest args)
  ;; ここにバックグラウンドで実行する処理を書く
  )

(lesque:enqueue *lesque*
                "my-queue"
                '(deferred-job "arg1" "arg2"))

これでRedisに記録されました。

別プロセスでワーカーを動かします。lesque-workerは別asdになっていることに気をつけてください。また、ジョブの関数 (この例ではdeferred-job) がこちらのプロセスでも有効である必要があります。

(ql:quickload :lesque-worker)

(lesque.worker:run "my-queue")

これでジョブがRedisにenqueueされる度にワーカーが処理してくれます。

失敗したジョブもRedisに記録され、lesque:all-failuresを実行すると失敗したジョブの情報がハッシュのリストで返って来ます。

おわりに

まだ実運用してないのでどんな感じかわかりませんが、まあまったく使い物にならんということは無いと思いますので、機会があればどうぞご利用ください。