新しいTokyo Tyrantで遊んでみて挫折した記録

mixi Engineers’ Blog » プラグインで独自ストレージを作ろうにて、

今回はTokyo Tyrant(TT)を使ってユーザ独自のストレージシステムを簡単に構築する方法について説明します。

プラグインで独自ストレージを作ろう – mixi Engineers' Blog

なんて、Tokyo TyrantがCレベルでプラグインを作れるようになったとのことで、すげーっ!!1てんで、さっそく遊んでみようと思いました。

ところで、Tokyo Tyrantで、ある単位で分割した複数のデータベースを扱いたいってなことがあるんじゃないかと思うのですよね。たとえば、データ量が多いとかメンテナンス性を確保するためとかで、ユーザごとにDBを作りたいとか、ある期間ごとにDBを分けたいとかいう場合等。Lua拡張でできたりする(?)のかちょっとわからないのですが、ともあれせっかくなので今回追加された機能を使ってあれこれやってみた……が、挫折。

やりたかったのは:

  • Tokyo Tyrant + Lux IO
  • さらに、複数DBに対応
    • dbname:keyというように、「:」でDB名とキーを区切ります。tcrmgrで実行する時は、以下のような感じ。
    • tcrmgr get localhost ‘antipop:foo_key’

ってな具合。パフォーマンスとかエラー処理とかは特に考慮せず、とりあえず動かそうというつもりで、結局動かなかったという。あと、C++とCが混ざると、どう書けばいいのかわからない……。

んで、下記のコードを下記環境でビルドしてみたものの、putはできてちゃんとデータが入ってるけど、getするとCPUが100%に張り付いたり、セグフォったりする。gdb使ってみたりしたけど、さっぱり状況がわかりません……。デバッグ力が足りなさ過ぎる……。↓のコードはなんか勘違いしててだめぽってるんだろうけど、しかしちゃんとうまく動けばとても簡単にあれこれできるし、素晴しい感じですね!!1

  • tokyocabinet-1.4.20
  • tokyotyrant-1.1.26
  • luxio-0.2.1
  • OS: Mac OSX 10.5.6
  • g++: i686apple-darwin9-g++-4.0.1 (GCC) 4.0.1 (Apple Inc. build 5484)
  • ビルドオプション: g++ -bundle -undefined error -lluxio -ltokyocabinet -o tt-multidb.so tt-multidb.cpp
  • TTの起動: ttserver -skel ./tt-multidb.so
  • TTへのアクセス:
#include <luxio/btree.h>
#include <map>
#include <string>
extern "C" {
  #include <tcutil.h>
  #include <tcadb.h>
  #include <string.h>
bool initialize(ADBSKEL *skel);
}
using namespace std;
typedef map<string, Lux::IO::Btree *> db_container_t;
typedef map<string, Lux::IO::Btree *>::iterator db_container_iterator_t;
db_container_t container;
string make_db_path(string name) {
string path(tcsprintf("./%s", name.c_str()));
return path;
}
string extract_name(const void *kbuf) {
string key(const_cast<char *>((char *)kbuf));
string::size_type index = key.find(':');
return key.substr(0, index);
}
string extract_key(const void *kbuf) {
string key(const_cast<char *>((char *)kbuf));
string::size_type index = key.find(':');
return key.substr(index + 1);
}
Lux::IO::Btree* create_db(db_container_t container, string name) {
Lux::IO::Btree* db = new Lux::IO::Btree(Lux::IO::NONCLUSTER);
if (!db) return false;
if (!db->open(make_db_path(name), Lux::IO::DB_CREAT)) return false;
container[name] = db;
return db;
}
bool tt_multidb_open(db_container_t container, const char *name){
return true;
}
void *tt_multidb_get(db_container_t container, const void *kbuf, int ksiz, int *sp) {
Lux::IO::Btree* db;
string name(extract_name(kbuf));
string key(extract_key(kbuf));
if (!(db = container[name])) db = create_db(container, name);
Lux::IO::data_t *value = db->get(key.c_str(), key.length());
if (value != NULL) {
return (void *)value->data;
}
return NULL;
}
bool tt_multidb_put(db_container_t container, const void *kbuf, int ksiz, const void *vbuf, int vsiz) {
Lux::IO::Btree* db;
string name(extract_name(kbuf));
string key_str(extract_key(kbuf));
string value((char *)vbuf);
Lux::IO::data_t key = {key_str.c_str(), key_str.length()};
Lux::IO::data_t val = {vbuf, vsiz};
if (!(db = container[name])) db = create_db(container, name);
return db->put(&key, &val);
}
bool tt_multidb_close(db_container_t container) {
db_container_iterator_t pos;
for (pos = container.begin(); pos != container.end();) {
container[pos->first]->close();
delete container[pos->first];
}
return true;
}
bool initialize(ADBSKEL *skel) {
skel->opq   = reinterpret_cast<void *>(&container);
skel->open  = (bool (*)(void *, const char *))tt_multidb_open;
skel->close = (bool (*)(void *))tt_multidb_close;
skel->get   = (void *(*)(void *, const void *, int, int *))tt_multidb_get;
skel->put   = (bool (*)(void *, const void *, int, const void *, int))tt_multidb_put;
return true;
}

3 Comments

  1. getの戻り値はmallocされた領域でなくてはいけない(呼び出し側でfreeされる)という仕様があるので、
    value->data を malloc+memcpy (もしくはtcmemdupとか)で複製しないといけません。
    詳しくはTCADBのAPIをご覧ください。
    あと、tcsprintの戻り値はmallocされた領域なので、呼び出し側でfreeしないとメモリリークになります。

  2. おお。ありがとうございます!!1やってみます!!1

  3. おお。ありがとうございます!!1やってみます!!1

Leave a Reply

Your email address will not be published. Required fields are marked *

CAPTCHA


© 2020 栗林健太郎

Theme by Anders NorénUp ↑