マルチスレッドでgroongaを使う

いきなりgroongaってなんぞやって感じですが、ElasticSearchみたいなもんです。ライブラリがCなのでCから高速に使いたいときはこっちを使うと楽です。日本人が開発してるので日本語完全対応もうれしい。もうちょっとドキュメント整理してほしいけど、、、

http://groonga.org/ja/docs/index.html

インストー

インストールは

sudo apt-get install groonga groonga-http groonga-server-gqtp

でできますが、詳しくはマニュアルを参照のこと。

(ちょっとおまけ)static ライブラリを作りたい

staticライブラリを作りたい時もありますよね?ありますよね?(白目
groongaはcmakeで楽ちんに作れますが、--help-property-listで出力してもstaticライブラリを作れそうなプロパティが出てきません。configureしてもいいけどどうしてもcmakeでやりたい。。。大丈夫。GRNG_EMBED=onとしてビルドすればできます。

まずはソースコードをダウンロードします。

git clone --recursive git@github.com:groonga/groonga.git

オプション付きでビルドします

cmake . -DGRNG_EMBED=on

libの下にlibgroonga.aができます。

ちなみにこれで作ったlibgroonga.aをリンクする際は一緒につくられるonigmoライブラリへのリンクもいっしょに追加しないといけません(onigmoを使わない場合は不要です)
例)

gcc -o sample main.c -L(groonga root)/lib -L(groonga root)/vendor/onigmo -lgroonga -ldl -lstdc++ -lonigmo -lz -lm

libstdc++, libmはbuild-essentialをインストールしたらだいたい一緒に入ると思うがlibdl, libzはlibltdl, zlib1gをインストールしないとだめみたい。

C-APIの基本的な使い方

C-APIは色々あるんだが、コマンドラインから使うのと同じ感じで使えるのが楽だと思う。というかまだよくわかっとらん。
使い方はこちらが詳しいです

qiita.com

マルチスレッドで使う

よーやく本題。
groongaはマルチスレッド対応で特になにか設定をする必要はないようなことを書いてあるが、よーく読むとgrn_ctxは各スレッドに対して一つ与えてやらないといけないらしい。
ためしにinsert/deleteをマルチスレッドでやるサンプルを書いてみます。

#include <stdlib.h>
#include <stdio.h>
#include <groonga.h>
#include <pthread.h>

#define DATA(str, key) sprintf(str, "[{¥"_key¥":¥"%d¥",¥"value¥":¥"value¥"}]", key);

grn_ctx ctx_insert;
grn_ctx ctx_delete;

grn_obj* open(grn_ctx* ctx, const char* path)
{
    grn_obj* db;
    int i = 0;
    //grn_ctxオブジェクトの初期化
    ctx = grn_ctx_open(0);
    grn_ctx_init(ctx, 0);
    //DBが存在しない場合は作成する。ある場合は開いてctxと関連付ける
    GRN_DB_OPEN_OR_CREATE(ctx, path, 0, db);
    //JSONで出力させる
    grn_ctx_set_output_type(ctx, GRN_CONTENT_JSON);
    return db;
}

int close(grn_ctx* ctx, grn_obj* db)
{
    grn_obj_close(ctx, db);
    return grn_ctx_fin(ctx);
}

int insert_record(grn_ctx* ctx, char* data)
{
    //レコードを追加する処理
}

int delete_record(grn_ctx* ctx, char* key)
{
    //レコードを冊書する処理
}

void *insert(void* arg)
{
    int i = 0;
    char str[128];
    for(i = 0; i < 100; i++)
   {
        DATA(str, i);
        insert_record(&ctx_insert, str);
   }
}
void *delete(void* arg)
{
    usleep(100);
    int i = 0;
    char index[2];
    for(i = 0; i < 100; i++)
    {
        sprintf(index, "%2d", i);
        delete_record(&ctx_delete, index);
    } 
}

int main(void)
{
    grn_init();
    grn_obj* db_insert = open(&ctx_insert, "db");
    //おなじDBなので同じオブジェクトを使い回せば良いのではと思ったが今のところうまく行ってないので
    grn_obj* db_delete = open(&ctx_delete, "db");

    //pthreadでスレッドを作り、joinさせて動かす
    pthread_t delete_th, insert_th;
    pthread_create(&insert_th, NULL, insert, NULL);
    pthread_create(&delete_th, NULL, delete, NULL);

    pthread_join(insert_th, NULL);
    pthread_join(delete_th, NULL);

   close(&ctx_insert, db_insert);
   close(&ctx_delete, db_delete);
   
    //一回で良い
    grn_fin();
    return 0;
}

割りと省略しまくりですが、大事なのはgrn_ctx, grn_dbをそれぞれのスレッドに対して作ること。