[Setsunaとは]
SetsunaはComplex Event Processing エンジンです。
複数のデータストリームに対してリアルタイムにSQL記述で
横断的に検索を行いデータの変化を掴み、利用者の
作成したイベントを実行できます。

データを蓄積するのではなく今、この瞬間のデータストリームに
対して検証を行うことができます。
ただし、過去一定時間のデータはメモリ上のテーブル構造に
保持しているため、時間軸を意識して一定期間のデータから
変化を見つけることも可能です。


[ライセンス]
Apache License, Version 2.0


[アーキテクチャ]
・Setsunaは大きく3つの要素で構成されています。

  1.Adapter
    Adapterは入力をSetsunaに接続する役目を担います。
    入力された情報を自動的に分解され、テーブルに格納されます。
    テーブルは入力されたデータを規定のセパレータで分解後
    それぞれカラムにマッピングされます。テーブル定義は指定することも
    出来ますし、自動でSetsunaに作らすこともできます。
    自動の場合はAdapterが最初に取得したデータを使ってテーブル定義が
    作成されます。

  2.Queruy
    データの変化を検証する役目を担います。
    Triggerという入力データのカラムに対してバインドする簡単な条件判定と
    QueryというSQL文指定での全アダプターからの入力に対する横断的な検証の
    2つで出来ています。
  
  3.Event
    Queryによりデータの変化を見つけた後に実行される処理です。
    ユーザが自由に作成したイベントを実行することが可能です。
    Linuxシェルでも構いませんし、Javaで作られたプログラムでも構いません。

・Setsunaが利用するデータベース
  Adapterから入力されて値をQueryで検証するためにSetsunaはデータを蓄積する
  データベースを持っています。
  データベースにはH2Database(http://www.h2database.com/html/main.html)を
  利用しています。H2DatabaseはSQLをサポートするリレーショナルデータべースです。

  Adapterからのデータは全てH2Databaseに格納されるので、Queryでは
  SQLを利用してデータの変化を調べることが可能になっています。
  そしてH2DatabaseはInMemoryデータべースを構築可能なためそれを利用しています。
  高速なデータの検証が可能になっています。

  Setsunaのデータベースに蓄積されるデータは一定時間で自動的に削除されていきます。
  そのためデータベースのクリーニングは必要ありません。(-atimeオプションにて
  データの生存時間を変更可能です)
  また、起動毎に新規で作りなおすため、データを永続的に利用することもできません。


[現在出来ること]
 現在のリリースバージョンではパイプ入力のAdapter部分を標準採用しています。
 つまり、なんだかのデータをパイプで繋いでSetsunaに流しこむことが標準で可能です。
 もしくは-serverオプションにてMessagePack-RPCプロトコルのサーバモードでの
 データ入力が可能です。

 また、1台のサーバ上で複数のSetsunaのプロセスを起動してそれぞれ個別でデータを
 流し込んだとしても全てのSetsunaプロセスから全てのデータが横断的にQuery可能です。
 これは内部でどれか1つのSetsunaプロセスが自動的にDBサーバになるからです。

 ですが、Setsuna自体がサーバとなり外部からネットワーク越しにデータを受け付ける
 ことはできません。この部分は今後開発予定です。
 またSetsuna自体を分散することも今後の開発課題です。


[利用方法]
1.前提
  検証OS:Linux(CentOS5.5以上)
         Windows(Xp, 7)
         Mac(Mac OS X 10.6)

  実行環境:JDK1.6以上

2.実行準備
  2.1.Javaが実行できる環境にしてください。
  2.2.実行可能 jar が付属していますので、それを利用します(zip展開後直下にあるsetsuna.jarです)
  ※./testディレクトリ内のスクリプトを実行する場合は、setsuna.jarをtestディレクトリに
    コピーしてから実行してください。

3.実行(CentOS5.5を想定)
  3.1.ヘルプの表示
  $java -jar setsuna.jar -help


 3.2.各種実行例
 Setsunaはかならずパイプからの標準入力か、-serverモードでの入力が必要です。
 これは標準入力の1データ単位でデータ検知=>イベント実行が動くためです。
 
 以下にLinuxの管理コマンドであるsarの結果を入力とした場合の簡単な利用例をいくつか示します
 ※1つのデータを分解するセパレータや、1データ区切りを決めるセパレータなどの変更方法は
   例の後に出てくるオプション一覧をご覧ください。

 例1) Setsunaはパイプラインに繋ぐだけの場合は受け取ったデータを内部的にどのような
      テーブル構造にマッピングしているかを標準出力にJSON形式で出力します。
   $sar -u 2 1000 | grep  --line-buffered all | java -jar setsuna.jar

   !!!!!!!!!!!!!!!!!!!!- 注意 -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
   なぜgrepを間に挟んでいるかというとCentOS5.5を利用してsarコマンドを実行した場合の
   sarからの出力結果は以下になります.
   $sar -u 2 1000
   Linux 2.6.18-194.el5 (localhost.localdomain)    2012年XX月XX日

   20時33分26秒       CPU     %user     %nice   %system   %iowait    %steal     %idle
   20時33分28秒       all      0.00      0.00      0.00      0.00      0.00    100.00
   20時33分30秒       all      0.00      0.00      0.00      0.00      0.00    100.00
   20時33分32秒       all      0.00      0.00      0.00      0.00      0.00    100.00
   -----------------------------------------------------------------------------------------
   上記のデータをそのままSetsunaに送り込むと1行目を利用してテーブル構造を構築していしまい、
   2行目以降のデータ項目の構造が異なるため、ただしく読み込めません。そこで、grepコマンドを
   利用してCPUの列に'all'という文字列が出力されている行だけ取り込んでいます。
   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

   取り込んだ後にSetsunaから出力された結果は以下になりました
   {"COLUMN0":"20時38分54秒","COLUMN1":"all","COLUMN2":"3.62","COLUMN3":"0.00","COLUMN4":"0.37","COLUMN5":"0.00","COLUMN6":"0.00","COLUMN7":"96.00"}
   {"COLUMN0":"20時38分56秒","COLUMN1":"all","COLUMN2":"21.53","COLUMN3":"0.00","COLUMN4":"0.88","COLUMN5":"0.00","COLUMN6":"0.00","COLUMN7":"77.60"}
   {"COLUMN0":"20時38分58秒","COLUMN1":"all","COLUMN2":"0.12","COLUMN3":"0.00","COLUMN4":"0.00","COLUMN5":"0.00","COLUMN6":"0.00","COLUMN7":"99.88"}
   JOSNフォーマットで'カラム名:データ'という形式です
   それぞれ以下のようにマッピングされています
   COLUMN0=時刻
   COLUMN1=CPU
   COLUMN2=%user
   COLUMN3=%nice
   COLUMN4=%system
   COLUMN5=%iowait
   COLUMN6=%steal
   COLUMN7=%idle
 
 
 例2) 受け取ったデータに対して、プロセッシングであるTriggerを適応してみましょう
      Triggerを利用する場合"-trigger"オプションを指定します。
      指定している条件はCOLUMN7にマッピングされているCPUのアイドル値が90%を下回っていることを条件にしています
      ※データがマッチした際に実行するユーザオリジナルのスクリプトを指定しない場合は標準出力に
        入力されたパイプ入力からのデータが出力されます
   $sar -u 2 10000 | grep  --line-buffered all | java -jar setsuna.jar -trigger "COLUMN7 < 90"
 
 
 例3) 受け取ったデータに対して、プロセッシングであるTriggerとQuery適応してみましょう
      Queryを利用する場合"-query"オプションを指定します。
      指定している条件はTriggerが例2と同様でQueryがCOLUMN0にマッピングされているsarの
      取得時間の降順で並べたデータの上位5件のデータ内のCOLUMN5にマッピングされているI/OWait値の平均が10以上であるかを検知しています。
      ※データがマッチした際に実行するユーザオリジナルのスクリプトを指定しない場合は標準出力に入力されたパイプ入力からのデータが出力されます
   $sar -u 2 10000 | grep  --line-buffered all | java -jar setsuna.jar -trigger "COLUMN7 < 90" -query "select * from (select avg(to_number(column5)) as avgio from (select column5 from pipe order by column0 desc limit 5) as t1)  where avgio > 10"
 
 
 例4) 次の例では例3でマッチした場合にオリジナルで作成したシェルスクリプトを実行してみましょう
      ユーザ作成のスクリプトを実行する場合"-event"オプションを指定します。
   $sar -u 2 10000 | grep  --line-buffered all | java -jar setsuna.jar -trigger "COLUMN7 < 90" -query "select * from (select avg(to_number(column5)) as avgio from (select column5 from pipe order by column0 desc limit 5) as t1)  where avgio > 10" -event /home/setsuna/WarningIOWait.sh
 
 
 例5) 次の例は応用例になります。ここまでは1つのインプットデータへ検索処理を行ってきましたが、
      複数のインプットデータを処理してみましょう。
      複数のインプットデータを処理するには、まず複数のパイプ入力をSetsunaに投入する必要があります。
 
      そのために2つのターミナルを起動してください。
       [ターミナル1]ではsarの結果をいままでどおり投入しています。そしてデータに'-stream'オプションを利用してsarという名前を付けています
 
        $sar -u 2 10000 | grep  --line-buffered all | java -jar setsuna.jar -stream sar
 
 
       [ターミナル2]ではtopの先頭行を投入しています。そしてそのデータにtopという名前を付けています
        さらに-triggerにてload averageの値(COLUMN11)が1以上であることを監視し
        さらに-queryにてターミナル1で投入しているsarのデータの直近I/OWaitの値が10以上であるかを検知しています。
        この-triggerと-queryの両方がマッチした場合に-eventで指定したユーザスクリプトを呼び出しています。
 
        $top -b -d 1 | grep --line-buffered ^top | java -jar setsuna.jar -stream top -trigger "COLUMN11 > 1" -query "select * from (select avg(to_number(column5)) as avgio from (select column5 from sar order by column0 desc limit 5) as t1)  where avgio > 10" -event /home/setsuna/WarningIOWait.sh
      このように、複数のインプットデータを横断的に探索することで複数のデータを組み合わせた複雑な検証も可能です
 
 
 
 
  ----------------------------
  --- 各オプション詳細一覧 ---
  ----------------------------
   ※特に重要なオプションは-server、-trigger -query -event -stream -sep -dstです
   ※デバッグ関係のオプションは-debug、-errorlogです
  
 -stream:標準入力・サーバモード入力から受取るデータの名前。ここで指定した名前で一時テーブルが作成されるため、-queryなどでこの指定値を利用する。
        **省略可能**
        ※省略した場合は"pipe"となる
        [指定例]
          -stream sartable
  
  
 -server:サーバモードの起動指定
         サーバモードで起動した場合、MessagePack-RPCで作られたサーバでデータの投入を待ち受ける
         MessagePack-RPCで作成されたクライアントでデータを投入することが出来る
         サーバ側で定義されているRPCメソッド定義は以下
         [メソッド定義]
          int next ( String[] sendData )
         ----------------------------------
         **省略可能**
         ※省略した場合はパイプ入力となる
         [指定例]
           -server true
  
  
 -bindaddr:サーバモードの起動時のサーバがバインドするアドレス
           サーバモードで起動した場合のみ有効
           **省略可能**
           ※省略した場合は0.0.0.0にバインドされる
           [指定例]
            -bindaddr 192.168.1.1
  
  
 -bindport:サーバモードの起動時のサーバが待ち受けるポート番号
           サーバモードで起動した場合のみ有効
           **省略可能**
           ※省略した場合は10028番で起動する
           [指定例]
            -bindport 10222
  
  
  
  
 -atime:標準入力・サーバモード入力から受取るデータが一時テーブル上に存在する有効期限を秒で指定。
        **省略可能**
        ※省略した場合は600秒
        [指定例]
          -atime 3600
  
  
 -column:標準入力・サーバモード入力から受取る情報をデータベースの一時テーブル情報にマッピング。
         するためのカラム情報の定義を指定。カラム名を半角スペース区切りで定義する
         **省略可能**
         ※省略した場合はCOLUMN0、COLUMN1、・・・と自動定義される
         [指定例]
          -column "DATETIME TYPE USERCPU SYSCPU NICR IOWAIT IDOLE"
  
  
 -sep:標準入力から受取る情報を、カラム単位に分解するためのセパレータ文字列。
      **省略可能**
      ※省略した場合は" "となる
      [指定例]
       例1) -sep ","
       例2) -sep "="
  
  
 -sept:標準入力から受取る情報を、カラム情報として
       マッピングするために分解するセパレータが2個以上続いた場合に1つとして扱う指定。
       **省略可能**
       [指定]
        1 = なにもしない
        2 = 2個以上セパレータが連続したな場合は1つとする(デフォルト)
  
  
 -dst:自身への標準入力データのレコードの区切りを指定。
      [扱う区切りの指定]
       1 = 改行(デフォルト)
           CRLF or LF
       2 = 時間
           標準入力からのインプットから次のインプットまでの間が100ミリ秒以上ある場合は区切りとする指定
  
  
 -skiperror:カラム定義の合わない入力データがきた場合にExceptionを発行せずに無視する
            **省略可能**
            ※省略した場合は定義違いの場合Exception発行してSetsunaを停止
            [指定例]
             -skiperror true
  
  
 -offset:データ読み込み時の開始位置。
         標準入力モード時や、サーバモード時に送り込まれたデータを指定されたデータ数
         スキップ(読み込まない)してから始めて内部DBへの格納を開始する
         利用イメージは読み込まれるデータの最初レコードに空白や、本来読み込みたいデータと異なる
         フォーマットが存在した場合に利用する
         このオプションを利用する場合は必ず-columnオプションも設定する必要がある
         これはデータを格納するテーブル定義を作成する必要があるためである
         **省略可能**
         [設定例]
          -offset 3 -column "ip time method result"
          ※投入されたデータを3レコードスキップ後、-column指定のカラム定義で取り込む
  
  
 -trigger:データの変化を検知するための第1要素
            この指定が適応可能なデータは自プロセスのSetsunaMainへの標準入力のデータだけである。
            この指定が適応される粒度は、標準入力からデータを-dst指定で区切った1インプット単位である
          カラム名を指定してそれに対しての条件を記述する形式となる。
            指定方法のフォーマットは[カラム名][条件][比べる値]
            [条件]は4つ存在する'>' or '<' or 'like'(部分一致) or '='(完全一致)
            条件の'<'や'>'は指定したカラムの値が数字ではない場合は、可能な限りに変換して比べます
            例えば'10%'や'LEVEL=90'のような数値を持ちながらその他の文字も連結されているような場合は数値のみを抜き出して比べる
          **省略可能**
          ※省略した場合はマッチしたものとして処理
          [指定例]
           例1)-trigger "COLUMN6 > 10"
           例2)-trigger "COLUMN6 like ABCD"
  
  
 -query:データの変化を検知するための第2要素
        -triggerの指定がない場合または、
        -trigger指定でマッチした場合に実行されるSQLクエリ。
          この指定が適応可能なデータは1つのサーバ内で稼働する全てのSetsunaMainへの標準入力のデータである。
          他のSetsunaMainへの標準入力は-streamで指定した名前でテーブルかされているので、SQL内でテーブル指定で利用可能である。
          この指定が適応される粒度は、標準入力からデータを-dst指定で区切った1インプット単位に
        -triggerが適応されそこでマッチした場合に実行される。実行対象となるテーブルデータは
          過去に標準入力から渡されたデータの有効期限内のデータ全てである。
        このSQLクエリの結果が1件でもあれば条件に完全にマッチしたとして、以降の-eventで
          指定されたイベントが実行される。データが1件でもあればという部分に注意が必要である。
          例えば'select count(*)'などはたとえ件数が0件でも0件という
          件数が返されるためイベントが実行されてします。
          通常の'select *'系であれば結果なしになるので、マッチするデータがなければイベントは実行されない。
          もしcount系で0件かどうかを指定したい場合は'-count true'を指定することで、取得された件数を判断するようになる。
        テーブル内には無条件でC_TIMEという登録時間をtimestamp型で持っているカラムと、PKEYIDXという登録順を表すBIGINTの値を必ず持っている
          このそれぞれの項目をSQL内で利用可能
        **省略可能**
        ※省略した場合はマッチしたものとして処理
  
        記述できる指定はSQLである。テーブル名や、カラム名は'-stream'や、'-column'で指定したものになる。
          また両方を省略している場合は、それぞれのDefault値でテーブル、カラムが作成されている。
        SQLの文法はH2Databaseに準拠している。また、H2Databaseには存在しない、以下の関数が利用可能である
         [利用可能追加関数]
          to_number(varchar)
            ※上記のto_numberは数値が含まれている値の場合は可能な限り数値化を試みます。
          to_char(number)
         [SQLの指定例]
          -query "select * from (select avg(to_number(COLUMN10)) as avgld from pipe order by COLUMN1 desc limit 10)) t1 where t1.avgld > 2"
  
  
 -count:-queryで指定したSQLがcount結果を返してくることを明示的に指定します
        この指定をおこなった場合は結果が1件以上の場合にユーザスクリプトが実行されるようになる
        [指定例]
         -count true
  
  
 -event:イベントで実行するスクリプト(シェルやbatなど)を指定
          フルパス指定を推奨
        指定されたスクリプトは-triggerか-queryで指定した全てがマッチした場合に実行される
        実行される粒度は標準入力から入ってくる1データ単位で-triggerと-queryがマッチした場合に
          1回づつ、別プロセスで実行される。
        また実行されるスクリプトの引数には、このイベントを実行するための基となった、標準入力からの
          1入力レコードが渡される。シェルであれば'$1'変数等で取得出来ることになる。
        [指定例]
         -event /home/setsuna/DataWriteEvent.sh
        [シェル実装例]
         #!/bin/sh
         echo $1 >> /var/tmp/event.log
  
  
 -eventquery:イベントを-event指定でのシェルやバッチではなく、任意のSQLを
             実行させその結果をJSONで標準出力に出力したい場合はこのオプションにSQLを記述する。
             [指定例]
              -eventquery "select * from pile"
  
  
 -argtype:-eventで指定したユーザイベントへの引数のフォーマットを指定
          [指定]
           JSON
           CSV
          [指定例]
           -argtype JSON
  
  
  
  
 -errorlog:Setsuna自身のエラー出力をコンソールではなく、指定したファイルに出力する
           出力先のファイル名を指定する
          [指定例]
           -errorlog "setsuna_error.log"
  
  
 -debug:Inputデータ文字列、実行した-trigger、実行したSQL、実行したユーザイベントコマンドを標準出力に出力する
        指定出来る種類は2種類あり、onとonlyである。
        on=デバッグ出力とSetsunaそのもののイベントの出力も混在して出力される
        only=デバッグ出力のみ出力。Setsunaそのもののイベントの出力はでない
        省略時はdebugなし
        [指定例]
         -debug on
         -debug only

[info]
バグ報告や機能要望などはta.okuyamaoo[at]gmail.comに連絡をお願いします。
twitterの場合は@okuyamaooです。