Tokyo Tyrantに追加された新機能(「mixi Engineers’ Blog » プラグインで独自ストレージを作ろう」)について「新しいTokyo Tyrantで遊んでみて挫折した記録」というエントリを書いた後、Tokyoシリーズ作者の平林さんのコメントがあったり(ありがとうございます!!1)、また、しょうもない見落しを修正したりしてとりあえず動くようになりました。こんな感じの仕様です。

  • ストレージにはLux IOを利用(社内で流行っていたので)
  • キーはdbname:keyという感じで、たとえばget antipop:fooとかすると、カレントディレクトリ(決め打ち)のantipopというDBのfooというキーのvalueをgetするという感じ。DBがなければ新規作成。また、frenchpop:fooとすると、同様にfrenchpopというDBのfooというキーのvalueをgetする。もちろん、そのふたつのfooは違うvalueを指す
    • データ量が多いとかメンテナンス性を確保するためとかで、ユーザごとにDBを作りたいとか、ある期間ごとにDBを分けたいとかいう場合を想定しています。

開発、実行したのは以下の通りの環境。

  • 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)
  • TTの起動: ttserver -skel ./tt-multidb.so
  • TTへのアクセス例:
    • tcrmgr put foo:bar baz # (fooというdbから、barキーでbazというvalueをput)
    • tcrmgr get foo:bar # (fooというdbから、barキーのvalueをget)

んでもって、ビルド。

$ g++ -bundle -undefined error -lluxio -ltokyocabinet -o tt-multidb.so tt-multidb.cpp

とりあえず動くように、というわけで、いろいろあんまり考えていません。そのあたり、いろいろ勉強しながらもうちょっとなんとかしたいなあと思ったりしています。

#include <map>
#include <string>
#include <luxio/btree.h>
extern "C" {
  #include <tcutil.h>
  #include <tcadb.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) {
char *cpath = tcsprintf("./%s", name.c_str());
string spath(cpath);
tcfree(cpath);
return spath;
}
string extract_name(const void *kbuf, int ksiz) {
string key((char *)kbuf);
int index = (int)key.find(':');
return key.substr(0, index);
}
string extract_key(const void *kbuf, int ksiz) {
string key((char *)kbuf);
int index = (int)key.find(':');
return key.substr(index + 1, (ksiz - 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, ksiz));
string key(extract_key(kbuf, ksiz));
if (!(db = container[name])) db = create_db(container, name);
Lux::IO::data_t *value = db->get(key.c_str(), key.length());
if (value != NULL) {
void *result = tcmemdup(value->data, value->size);
*sp = value->size;
db->clean_data(value);
return result;
}
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, ksiz));
string skey(extract_key(kbuf, ksiz));
string value((char *)vbuf);
Lux::IO::data_t key = {skey.c_str(), skey.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(); ++pos) {
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;
}