ハンドリング・フェーズでのプラグインAPI

概要

各々のDroonga Engineプラグインは、それ自身のためのハンドラーを持つことができます。ハンドリング・フェーズでは、ハンドラーはリクエストを処理して結果を返すことができます。

ハンドラーの定義の仕方

例えば、「foo」という名前のプラグインにハンドラーを定義する場合は以下のようにします:

require "droonga/plugin"

module Droonga::Plugins::FooPlugin
  extend Plugin
  register("foo")

  define_single_step do |step|
    step.name = "foo"
    step.handler = :Handler
    step.collector = Collectors::And
  end

  class Handler < Droonga::Handler
    def handle(message)
      # リクエストを処理するための操作
    end
  end
end

ハンドラーを定義するための手順は以下の通りです:

  1. プラグイン用のモジュール(例:Droonga::Plugins::FooPlugin)を定義し、プラグインとして登録する。(必須)
  2. Droonga::SingleStepDefinitionを使い、実装しようとしているハンドラーに対応する「single step」を定義する。(必須)
  3. Droonga::Handlerを継承したハンドラークラス(例:Droonga::Plugins::FooPlugin::Handler)を定義する。(必須)
  4. リクエストを処理する操作を#handleとして定義する。(任意)

プラグイン開発チュートリアルも併せて参照して下さい。

ハンドラーはどのように動作するか

ハンドラーは以下のように動作します:

  1. Droonga Engineが起動する。
    • stepとハンドラークラスが登録される。
    • Droonga Engineが起動し、入力メッセージを待ち受ける。
  2. 適合フェーズからメッセージが転送されてくる。 この時点で処理フェーズが開始される。
    • Droonga Engineが、メッセージタイプからstepの定義を見つける。
    • Droonga Engineが、登録済みの定義に従ってsingle stepを作成する。
    • single stepが、登録済みのハンドラークラスのインスタンスを作成する。 この時点でハンドリング・フェーズが開始される。
      • ハンドラーの#handleメソッドが、リクエストの情報を含むタスクメッセージを伴って呼ばれる。
        • このメソッドにより、入力メッセージを任意に処理することができる。
        • このメソッドは、処理結果の出力を戻り値として返す。
      • ハンドラーの処理が完了した時点で、そのタスクメッセージ(およびリクエスト)のハンドリング・フェーズが終了する。
    • メッセージタイプからstepが見つからなかった場合は、何も処理されない。
    • すべてのstepが処理を終えた時点で、そのリクエストに対する処理フェーズが終了する。

上記の通り、Droonga Engineは各リクエストに対してその都度ハンドラークラスのインスタンスを生成します。

ハンドラー内で発生したすべてのエラーは、Droonga Engine自身によって処理されます。エラー処理も併せて参照して下さい。

設定

action.synchronous (真偽値, 省略可能, 初期値=false)
リクエストを同期的に処理する必要があるかどうかを示す。 例えば、テーブル内に新しいカラムを追加するリクエストは、テーブルが存在しない場合には必ず、テーブル作成用のリクエストの後で処理する必要がある。このような場合のハンドラーは、 action.synchronous = true の指定を伴うことになる。

クラスとメソッド

Droonga::SingleStepDefinition

このクラスは、ハンドラーに対応するstepの詳細を記述する機能を提供します。

#name, #name=(name)

step自身の名前を記述します。値は文字列です。

Droonga Engineは、メッセージのtypeに一致するnameを持つstepが存在する場合に、入力メッセージをコマンドのリクエストとして扱います。 言い換えると、このメソッドはstepに対応するコマンドの名前を定義します。

#handler, #handler=(handler)

特定のハンドラークラスをstepに紐付けます。 ハンドラークラスは以下のいずれかの方法で指定します:

ハンドラークラスをシンボルまたは文字列で指定した場合、参照先のクラスは、Droonga Engineが実際にそのstepを処理する時点までの間に定義しておく必要があります。 Droonga Engineがハンドラークラスの実体を見つけられなかった場合、またはハンドラークラスが未指定の場合には、Droonga Engineはそのリクエストに対して何も処理を行いません。

#collector, #collector=(collector)

特定のコレクタークラスをstepに紐付けます。 コレクタークラスは以下のいずれかの方法で指定します:

コレクタークラスをシンボルまたは文字列で指定した場合、参照先のクラスは、Droonga Engineが実際にそのstepの結果を集約する時点までの間に定義しておく必要があります。 Droonga Engineがコレクタークラスの実体を見つけられなかった場合、またはコレクタークラスが未指定の場合には、Droonga Engineは処理結果を集約せず、複数のメッセージとして返します。

コレクターの説明も併せて参照して下さい。

#write, #write=(write)

stepがストレージ内の情報を変更し得るかどうかを記述します。 リクエストがストレージ内のデータを変更することを意図する物である場合、そのリクエストはすべてのreplicaで処理される必要があります。 それ以外の場合、Droonga Engineは結果をキャッシュしたり、CPUやメモリの使用量を削減するなどして、処理を最適化することができます。

取り得る値:

#inputs, #inputs=(inputs)

(未稿)

#output, #output=(output)

(未稿)

Droonga::Handler

これはすべてのハンドラーに共通の基底クラスです。独自プラグインのハンドラークラスは、このクラスを継承する必要があります。

#handle(message)

このメソッドは、Droonga::HandlerMessageでラップされたタスクメッセージを受け取ります。 プラグインは、このタスクメッセージのメソッドからリクエストの情報を読み取る事ができます。

この基底クラスにおいて、このメソッドは何もしない単なるプレースホルダとして定義されています。 メッセージを処理するには、以下のようにメソッドを再定義して下さい:

module Droonga::Plugins::MySearch
  class Handler < Droonga::Handler
    def handle(message)
      search_query = message.request["body"]["query"]
      ...
      { ... } # the result
    end
  end
end

Droonga Engineは、このメソッドの戻り値を処理の結果として扱います。 結果の値は、レスポンスのbodyの組み立てに使われ、Protocol Adapterに送られます。

Droonga::HandlerMessage

このクラスはタスクメッセージに対するラッパーとして働きます。

Droonga Engineは送られてきたリクエストのメッセージを解析し、そのリクエストを処理するための複数のタスクメッセージを作成します。 1つのタスクメッセージは、リクエストの実体、step、後続するタスクの一覧などの情報を持ちます。

#request

このメソッドはリクエストメッセージを返します。例:

module Droonga::Plugins::MySearch
  class Handler < Droonga::Handler
    def handle(message)
      request = message.request
      search_query = request["body"]["query"]
      ...
    end
  end
end

@context

対応するボリュームのストレージを示す、Groonga::Contextのインスタンスへの参照。 Rroongaのクラスリファレンスも併せて参照して下さい

@contextを経由して、Rroongaのすべての機能を利用できます。 例えば、以下は指定されたテーブルのすべてのレコードの数を返す例です:

module Droonga::Plugins::CountRecords
  class Handler < Droonga::Handler
    def handle(message)
      request = message.request
      table_name = request["body"]["table"]
      count = @context[table_name].size
    end
  end
end