Summary: implemented a commond line shell to talk with leveldb thrift server, which is based on state pattern and can be easily extended. Test Plan: build and run Reviewers: dhruba, zshao, heyongqiang Differential Revision: https://reviews.facebook.net/D4713main
parent
c3096afd61
commit
2b443ba887
@ -0,0 +1,271 @@ |
|||||||
|
|
||||||
|
#include <boost/shared_ptr.hpp> |
||||||
|
|
||||||
|
#include "DBClientProxy.h" |
||||||
|
|
||||||
|
|
||||||
|
#include "thrift/lib/cpp/protocol/TBinaryProtocol.h" |
||||||
|
#include "thrift/lib/cpp/transport/TSocket.h" |
||||||
|
#include "thrift/lib/cpp/transport/TTransportUtils.h" |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
using namespace std; |
||||||
|
using namespace boost; |
||||||
|
using namespace Tleveldb; |
||||||
|
using namespace apache::thrift::protocol; |
||||||
|
using namespace apache::thrift::transport; |
||||||
|
|
||||||
|
namespace leveldb { |
||||||
|
|
||||||
|
DBClientProxy::DBClientProxy(const string & host, int port) : |
||||||
|
host_(host), |
||||||
|
port_(port), |
||||||
|
dbToHandle_(), |
||||||
|
dbClient_() { |
||||||
|
} |
||||||
|
|
||||||
|
DBClientProxy::~DBClientProxy() { |
||||||
|
cleanUp(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void DBClientProxy::connect(void) { |
||||||
|
cleanUp(); |
||||||
|
printf("Connecting to %s:%d\n", host_.c_str(), port_); |
||||||
|
try { |
||||||
|
boost::shared_ptr<TSocket> socket(new TSocket(host_, port_)); |
||||||
|
boost::shared_ptr<TTransport> transport(new TBufferedTransport(socket)); |
||||||
|
boost::shared_ptr<TBinaryProtocol> protocol(new TBinaryProtocol(transport)); |
||||||
|
dbClient_.reset(new DBClient(protocol)); |
||||||
|
|
||||||
|
transport->open(); |
||||||
|
} catch (const std::exception & e) { |
||||||
|
dbClient_.reset(); |
||||||
|
throw; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void DBClientProxy::cleanUp(void) { |
||||||
|
if(dbClient_.get()) { |
||||||
|
for(map<string, DBHandle>::iterator itor = dbToHandle_.begin(); |
||||||
|
itor != dbToHandle_.end(); |
||||||
|
++itor) { |
||||||
|
dbClient_->Close(itor->second, itor->first); |
||||||
|
} |
||||||
|
dbClient_.reset(); |
||||||
|
} |
||||||
|
dbToHandle_.clear(); |
||||||
|
} |
||||||
|
|
||||||
|
void DBClientProxy::open(const string & db) { |
||||||
|
if(!dbClient_.get()) { |
||||||
|
printf("please connect() first\n"); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// printf("opening database : %s\n", db.c_str());
|
||||||
|
// we use default DBOptions here
|
||||||
|
DBOptions opt; |
||||||
|
DBHandle handle; |
||||||
|
try { |
||||||
|
dbClient_->Open(handle, db, opt); |
||||||
|
} catch (const LeveldbException & e) { |
||||||
|
printf("%s\n", e.message.c_str()); |
||||||
|
if(kIOError == e.errorCode) { |
||||||
|
printf("no such database : %s\n", db.c_str()); |
||||||
|
return; |
||||||
|
}else { |
||||||
|
printf("Unknown error : %d\n", e.errorCode); |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
dbToHandle_[db] = handle; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
bool DBClientProxy::create(const string & db) { |
||||||
|
if(!dbClient_.get()) { |
||||||
|
printf("please connect() first\n"); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
printf("creating database : %s\n", db.c_str()); |
||||||
|
DBOptions opt; |
||||||
|
opt.create_if_missing = true; |
||||||
|
opt.error_if_exists = true; |
||||||
|
DBHandle handle; |
||||||
|
try { |
||||||
|
dbClient_->Open(handle, db, opt); |
||||||
|
}catch (const LeveldbException & e) { |
||||||
|
printf("%s\n", e.message.c_str()); |
||||||
|
printf("error code : %d\n", e.errorCode); |
||||||
|
if(kNotFound == e.errorCode) { |
||||||
|
printf("no such database : %s\n", db.c_str()); |
||||||
|
return false;; |
||||||
|
} else { |
||||||
|
printf("Unknown error : %d\n", e.errorCode); |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
dbToHandle_[db] = handle; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
map<string, DBHandle>::iterator |
||||||
|
DBClientProxy::getHandle(const string & db) { |
||||||
|
map<string, DBHandle>::iterator itor = dbToHandle_.find(db); |
||||||
|
if(dbToHandle_.end() == itor) { |
||||||
|
open(db); |
||||||
|
itor = dbToHandle_.find(db); |
||||||
|
} |
||||||
|
|
||||||
|
return itor; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
bool DBClientProxy::get(const string & db, |
||||||
|
const string & key, |
||||||
|
string & value) { |
||||||
|
if(!dbClient_.get()) { |
||||||
|
printf("please connect() first\n"); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
map<string, DBHandle>::iterator itor = getHandle(db); |
||||||
|
if(dbToHandle_.end() == itor) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
ResultItem ret; |
||||||
|
Slice k; |
||||||
|
k.data = key; |
||||||
|
k.size = key.size(); |
||||||
|
// we use default values of options here
|
||||||
|
ReadOptions opt; |
||||||
|
dbClient_->Get(ret, |
||||||
|
itor->second, |
||||||
|
k, |
||||||
|
opt); |
||||||
|
if(kOk == ret.status) { |
||||||
|
value = ret.value.data; |
||||||
|
return true; |
||||||
|
} else if(kNotFound == ret.status) { |
||||||
|
printf("no such key : %s\n", key.c_str()); |
||||||
|
return false; |
||||||
|
} else { |
||||||
|
printf("get data error : %d\n", ret.status); |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool DBClientProxy::put(const string & db, |
||||||
|
const string & key, |
||||||
|
const string & value) { |
||||||
|
if(!dbClient_.get()) { |
||||||
|
printf("please connect() first\n"); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
map<string, DBHandle>::iterator itor = getHandle(db); |
||||||
|
if(dbToHandle_.end() == itor) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
kv temp; |
||||||
|
temp.key.data = key; |
||||||
|
temp.key.size = key.size(); |
||||||
|
temp.value.data = value; |
||||||
|
temp.value.size = value.size(); |
||||||
|
WriteOptions opt; |
||||||
|
opt.sync = true; |
||||||
|
Code code; |
||||||
|
code = dbClient_->Put(itor->second, |
||||||
|
temp, |
||||||
|
opt); |
||||||
|
|
||||||
|
|
||||||
|
if(kOk == code) { |
||||||
|
// printf("set value finished\n");
|
||||||
|
return true; |
||||||
|
} else { |
||||||
|
printf("put data error : %d\n", code); |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
bool DBClientProxy::scan(const string & db, |
||||||
|
const string & start_key, |
||||||
|
const string & end_key, |
||||||
|
const string & limit, |
||||||
|
vector<pair<string, string> > & kvs) { |
||||||
|
if(!dbClient_.get()) { |
||||||
|
printf("please connect() first\n"); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
int limitInt = -1; |
||||||
|
limitInt = atoi(limit.c_str()); |
||||||
|
if(limitInt <= 0) { |
||||||
|
printf("Error while parse limit : %s\n", limit.c_str()); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if(start_key > end_key) { |
||||||
|
printf("empty range.\n"); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
map<string, DBHandle>::iterator itor = getHandle(db); |
||||||
|
if(dbToHandle_.end() == itor) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
ResultIterator ret; |
||||||
|
// we use the default values of options here
|
||||||
|
ReadOptions opt; |
||||||
|
Slice k; |
||||||
|
k.data = start_key; |
||||||
|
k.size = start_key.size(); |
||||||
|
dbClient_->NewIterator(ret, |
||||||
|
itor->second, |
||||||
|
opt, |
||||||
|
seekToKey, |
||||||
|
k); |
||||||
|
Iterator it; |
||||||
|
if(kOk == ret.status) { |
||||||
|
it = ret.iterator; |
||||||
|
} else { |
||||||
|
printf("get iterator error : %d\n", ret.status); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
int idx = 0; |
||||||
|
string ck = start_key; |
||||||
|
while(idx < limitInt && ck < end_key) { |
||||||
|
ResultPair retPair; |
||||||
|
dbClient_->GetNext(retPair, itor->second, it); |
||||||
|
if(kOk == retPair.status) { |
||||||
|
++idx; |
||||||
|
ck = retPair.keyvalue.key.data; |
||||||
|
if (ck < end_key) { |
||||||
|
kvs.push_back(make_pair(retPair.keyvalue.key.data, |
||||||
|
retPair.keyvalue.value.data)); |
||||||
|
} |
||||||
|
} else if(kEnd == retPair.status) { |
||||||
|
printf("not enough values\n"); |
||||||
|
return true; |
||||||
|
} else { |
||||||
|
printf("GetNext() error : %d\n", retPair.status); |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace
|
@ -0,0 +1,64 @@ |
|||||||
|
|
||||||
|
#ifndef TOOLS_SHELL_DBCLIENTPROXY |
||||||
|
#define TOOLS_SHELL_DBCLIENTPROXY |
||||||
|
|
||||||
|
#include <vector> |
||||||
|
#include <map> |
||||||
|
#include <string> |
||||||
|
#include <boost/utility.hpp> |
||||||
|
#include <boost/shared_ptr.hpp> |
||||||
|
|
||||||
|
#include "DB.h" |
||||||
|
|
||||||
|
/*
|
||||||
|
* class DBClientProxy maintains: |
||||||
|
* 1. a connection to leveldb service |
||||||
|
* 2. a map from db names to opened db handles |
||||||
|
* |
||||||
|
* it's client codes' responsibility to catch all possible exceptions. |
||||||
|
*/ |
||||||
|
|
||||||
|
namespace leveldb { |
||||||
|
|
||||||
|
class DBClientProxy : private boost::noncopyable { |
||||||
|
public: |
||||||
|
// connect to host_:port_
|
||||||
|
void connect(void); |
||||||
|
|
||||||
|
// return true on success, false otherwise
|
||||||
|
bool get(const std::string & db, |
||||||
|
const std::string & key, |
||||||
|
std::string & value); |
||||||
|
|
||||||
|
// return true on success, false otherwise
|
||||||
|
bool put(const std::string & db, |
||||||
|
const std::string & key, |
||||||
|
const std::string & value); |
||||||
|
|
||||||
|
// return true on success, false otherwise
|
||||||
|
bool scan(const std::string & db, |
||||||
|
const std::string & start_key, |
||||||
|
const std::string & end_key, |
||||||
|
const std::string & limit, |
||||||
|
std::vector<std::pair<std::string, std::string> > & kvs); |
||||||
|
|
||||||
|
// return true on success, false otherwise
|
||||||
|
bool create(const std::string & db); |
||||||
|
|
||||||
|
DBClientProxy(const std::string & host, int port); |
||||||
|
~DBClientProxy(); |
||||||
|
|
||||||
|
private: |
||||||
|
// some internal help functions
|
||||||
|
void cleanUp(void); |
||||||
|
void open(const std::string & db); |
||||||
|
std::map<std::string, Tleveldb::DBHandle>::iterator getHandle(const std::string & db); |
||||||
|
|
||||||
|
const std::string host_; |
||||||
|
const int port_; |
||||||
|
std::map<std::string, Tleveldb::DBHandle> dbToHandle_; |
||||||
|
boost::shared_ptr<Tleveldb::DBClient> dbClient_; |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace
|
||||||
|
#endif |
@ -0,0 +1,8 @@ |
|||||||
|
|
||||||
|
|
||||||
|
#include "ShellContext.h" |
||||||
|
|
||||||
|
int main(int argc, char ** argv) { |
||||||
|
ShellContext c(argc, argv); |
||||||
|
c.run(); |
||||||
|
} |
@ -0,0 +1,104 @@ |
|||||||
|
|
||||||
|
#include <iostream> |
||||||
|
#include <boost/shared_ptr.hpp> |
||||||
|
|
||||||
|
#include "ShellContext.h" |
||||||
|
#include "ShellState.h" |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include "thrift/lib/cpp/protocol/TBinaryProtocol.h" |
||||||
|
#include "thrift/lib/cpp/transport/TSocket.h" |
||||||
|
#include "thrift/lib/cpp/transport/TTransportUtils.h" |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
using namespace std; |
||||||
|
using namespace boost; |
||||||
|
using namespace Tleveldb; |
||||||
|
using namespace leveldb; |
||||||
|
using namespace apache::thrift::protocol; |
||||||
|
using namespace apache::thrift::transport; |
||||||
|
|
||||||
|
void ShellContext::changeState(ShellState * pState) { |
||||||
|
pShellState_ = pState; |
||||||
|
} |
||||||
|
|
||||||
|
void ShellContext::stop(void) { |
||||||
|
exit_ = true; |
||||||
|
} |
||||||
|
|
||||||
|
bool ShellContext::ParseInput(void) { |
||||||
|
if(argc_ != 3) { |
||||||
|
printf("leveldb_shell host port\n"); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
port_ = atoi(argv_[2]); |
||||||
|
if(port_ <= 0) { |
||||||
|
printf("Error while parse port : %s\n", argv_[2]); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
clientProxy_.reset(new DBClientProxy(argv_[1], port_)); |
||||||
|
if(!clientProxy_.get()) { |
||||||
|
return false; |
||||||
|
} else { |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void ShellContext::connect(void) { |
||||||
|
clientProxy_->connect(); |
||||||
|
} |
||||||
|
|
||||||
|
void ShellContext::create(const string & db) { |
||||||
|
if (clientProxy_->create(db)) { |
||||||
|
printf("%s created\n", db.c_str()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void ShellContext::get(const string & db, |
||||||
|
const string & key) { |
||||||
|
string v; |
||||||
|
if (clientProxy_->get(db, key, v)) { |
||||||
|
printf("%s\n", v.c_str()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void ShellContext::put(const string & db, |
||||||
|
const string & key, |
||||||
|
const string & value) { |
||||||
|
if (clientProxy_->put(db, key, value)) { |
||||||
|
printf("(%s, %s) has been set\n", key.c_str(), value.c_str()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void ShellContext::scan(const string & db, |
||||||
|
const string & start_key, |
||||||
|
const string & end_key, |
||||||
|
const string & limit) { |
||||||
|
vector<pair<string, string> > kvs; |
||||||
|
if (clientProxy_->scan(db, start_key, end_key, limit, kvs)) { |
||||||
|
for(unsigned int i = 0; i < kvs.size(); ++i) { |
||||||
|
printf("%d (%s, %s)\n", i, kvs[i].first.c_str(), kvs[i].second.c_str()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void ShellContext::run(void) { |
||||||
|
while(!exit_) { |
||||||
|
pShellState_->run(this); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
ShellContext::ShellContext(int argc, char ** argv) : |
||||||
|
pShellState_(ShellStateStart::getInstance()), |
||||||
|
exit_(false), |
||||||
|
argc_(argc), |
||||||
|
argv_(argv), |
||||||
|
port_(-1), |
||||||
|
clientProxy_() { |
||||||
|
} |
||||||
|
|
||||||
|
|
@ -0,0 +1,51 @@ |
|||||||
|
#ifndef TOOLS_SHELL_SHELLCONTEXT |
||||||
|
#define TOOLS_SHELL_SHELLCONTEXT |
||||||
|
|
||||||
|
#include <map> |
||||||
|
#include <string> |
||||||
|
#include <boost/utility.hpp> |
||||||
|
#include <boost/shared_ptr.hpp> |
||||||
|
|
||||||
|
#include "DB.h" |
||||||
|
#include "DBClientProxy.h" |
||||||
|
|
||||||
|
class ShellState; |
||||||
|
|
||||||
|
class ShellContext : private boost::noncopyable { |
||||||
|
public: |
||||||
|
void changeState(ShellState * pState); |
||||||
|
|
||||||
|
void stop(void); |
||||||
|
|
||||||
|
bool ParseInput(void); |
||||||
|
|
||||||
|
void connect(void); |
||||||
|
|
||||||
|
void get(const std::string & db, |
||||||
|
const std::string & key); |
||||||
|
|
||||||
|
void put(const std::string & db, |
||||||
|
const std::string & key, |
||||||
|
const std::string & value); |
||||||
|
|
||||||
|
void scan(const std::string & db, |
||||||
|
const std::string & start_key, |
||||||
|
const std::string & end_key, |
||||||
|
const std::string & limit); |
||||||
|
|
||||||
|
void create(const std::string & db); |
||||||
|
|
||||||
|
void run(void); |
||||||
|
|
||||||
|
ShellContext(int argc, char ** argv); |
||||||
|
|
||||||
|
private: |
||||||
|
ShellState * pShellState_; |
||||||
|
bool exit_; |
||||||
|
int argc_; |
||||||
|
char ** argv_; |
||||||
|
int port_; |
||||||
|
boost::shared_ptr<leveldb::DBClientProxy> clientProxy_; |
||||||
|
}; |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,139 @@ |
|||||||
|
#include <iostream> |
||||||
|
#include <string> |
||||||
|
#include <sstream> |
||||||
|
#include <vector> |
||||||
|
|
||||||
|
#include "ShellState.h" |
||||||
|
#include "ShellContext.h" |
||||||
|
#include "transport/TTransportException.h" |
||||||
|
|
||||||
|
using namespace std; |
||||||
|
|
||||||
|
using namespace apache::thrift::transport; |
||||||
|
|
||||||
|
const char * PMT = ">> "; |
||||||
|
|
||||||
|
|
||||||
|
void ShellStateStart::run(ShellContext * c) { |
||||||
|
if(!c->ParseInput()) { |
||||||
|
c->changeState(ShellStateStop::getInstance()); |
||||||
|
} else { |
||||||
|
c->changeState(ShellStateConnecting::getInstance()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void ShellStateStop::run(ShellContext * c) { |
||||||
|
c->stop(); |
||||||
|
} |
||||||
|
|
||||||
|
void ShellStateConnecting::run(ShellContext * c) { |
||||||
|
try { |
||||||
|
c->connect(); |
||||||
|
} catch (const TTransportException & e) { |
||||||
|
cout << e.what() << endl; |
||||||
|
c->changeState(ShellStateStop::getInstance()); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
c->changeState(ShellStateConnected::getInstance()); |
||||||
|
} |
||||||
|
|
||||||
|
void ShellStateConnected::unknownCmd(void) { |
||||||
|
cout << "Unknown command!" << endl; |
||||||
|
cout << "Use help to list all available commands" << endl; |
||||||
|
} |
||||||
|
|
||||||
|
void ShellStateConnected::helpMsg(void) { |
||||||
|
cout << "Currently supported commands:" << endl; |
||||||
|
cout << "create db" << endl; |
||||||
|
cout << "get db key" << endl; |
||||||
|
cout << "scan db start_key end_key limit" << endl; |
||||||
|
cout << "put db key value" << endl; |
||||||
|
cout << "exit/quit" << endl; |
||||||
|
} |
||||||
|
|
||||||
|
void ShellStateConnected::handleConError(ShellContext * c) { |
||||||
|
cout << "Connection down" << endl; |
||||||
|
cout << "Reconnect ? (y/n) :" << endl; |
||||||
|
string s; |
||||||
|
while(getline(cin, s)) { |
||||||
|
if("y" == s) { |
||||||
|
c->changeState(ShellStateConnecting::getInstance()); |
||||||
|
break; |
||||||
|
} else if("n" == s) { |
||||||
|
c->changeState(ShellStateStop::getInstance()); |
||||||
|
break; |
||||||
|
} else { |
||||||
|
cout << "Reconnect ? (y/n) :" << endl; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void ShellStateConnected::run(ShellContext * c) { |
||||||
|
string line; |
||||||
|
cout << PMT; |
||||||
|
getline(cin, line); |
||||||
|
istringstream is(line); |
||||||
|
vector<string> params; |
||||||
|
string param; |
||||||
|
while(is >> param) { |
||||||
|
params.push_back(param); |
||||||
|
} |
||||||
|
|
||||||
|
// empty input line
|
||||||
|
if(params.empty()) |
||||||
|
return; |
||||||
|
|
||||||
|
if("quit" == params[0] || "exit" == params[0]) { |
||||||
|
c->changeState(ShellStateStop::getInstance()); |
||||||
|
} else if("get" == params[0]) { |
||||||
|
if(params.size() == 3) { |
||||||
|
try { |
||||||
|
c->get(params[1], params[2]); |
||||||
|
} catch (const TTransportException & e) { |
||||||
|
cout << e.what() << endl; |
||||||
|
handleConError(c); |
||||||
|
} |
||||||
|
} else { |
||||||
|
unknownCmd(); |
||||||
|
} |
||||||
|
} else if("create" == params[0]) { |
||||||
|
if(params.size() == 2) { |
||||||
|
try { |
||||||
|
c->create(params[1]); |
||||||
|
} catch (const TTransportException & e) { |
||||||
|
cout << e.what() << endl; |
||||||
|
handleConError(c); |
||||||
|
} |
||||||
|
} else { |
||||||
|
unknownCmd(); |
||||||
|
} |
||||||
|
}else if("put" == params[0]) { |
||||||
|
if(params.size() == 4) { |
||||||
|
try { |
||||||
|
c->put(params[1], params[2], params[3]); |
||||||
|
} catch (const TTransportException & e) { |
||||||
|
cout << e.what() << endl; |
||||||
|
handleConError(c); |
||||||
|
} |
||||||
|
} else { |
||||||
|
unknownCmd(); |
||||||
|
} |
||||||
|
} else if("scan" == params[0]) { |
||||||
|
if(params.size() == 5) { |
||||||
|
try { |
||||||
|
c->scan(params[1], params[2], params[3], params[4]); |
||||||
|
} catch (const TTransportException & e) { |
||||||
|
cout << e.what() << endl; |
||||||
|
handleConError(c); |
||||||
|
} |
||||||
|
} else { |
||||||
|
unknownCmd(); |
||||||
|
} |
||||||
|
} else if("help" == params[0]) { |
||||||
|
helpMsg(); |
||||||
|
} else { |
||||||
|
unknownCmd(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,87 @@ |
|||||||
|
|
||||||
|
#ifndef TOOLS_SHELL_SHELLSTATE |
||||||
|
#define TOOLS_SHELL_SHELLSTATE |
||||||
|
|
||||||
|
class ShellContext; |
||||||
|
|
||||||
|
/*
|
||||||
|
* Currently, there are four types of state in total |
||||||
|
* 1. start state: the first state the program enters |
||||||
|
* 2. connecting state: the program try to connnect to a leveldb server, whose |
||||||
|
* previous states could be "start" or "connected" states |
||||||
|
* 3. connected states: the program has already connected to a server, and is |
||||||
|
* processing user commands |
||||||
|
* 4. stop state: the last state the program enters, do some cleanning up things |
||||||
|
*/ |
||||||
|
|
||||||
|
class ShellState { |
||||||
|
public: |
||||||
|
virtual void run(ShellContext *) = 0; |
||||||
|
virtual ~ShellState() {} |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
class ShellStateStart : public ShellState { |
||||||
|
public: |
||||||
|
static ShellStateStart * getInstance(void) { |
||||||
|
static ShellStateStart instance; |
||||||
|
return &instance; |
||||||
|
} |
||||||
|
|
||||||
|
virtual void run(ShellContext *); |
||||||
|
|
||||||
|
private: |
||||||
|
ShellStateStart() {} |
||||||
|
virtual ~ShellStateStart() {} |
||||||
|
}; |
||||||
|
|
||||||
|
class ShellStateStop : public ShellState { |
||||||
|
public: |
||||||
|
static ShellStateStop * getInstance(void) { |
||||||
|
static ShellStateStop instance; |
||||||
|
return &instance; |
||||||
|
} |
||||||
|
|
||||||
|
virtual void run(ShellContext *); |
||||||
|
|
||||||
|
private: |
||||||
|
ShellStateStop() {} |
||||||
|
virtual ~ShellStateStop() {} |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
class ShellStateConnecting : public ShellState { |
||||||
|
public: |
||||||
|
static ShellStateConnecting * getInstance(void) { |
||||||
|
static ShellStateConnecting instance; |
||||||
|
return &instance; |
||||||
|
} |
||||||
|
|
||||||
|
virtual void run(ShellContext *); |
||||||
|
|
||||||
|
private: |
||||||
|
ShellStateConnecting() {} |
||||||
|
virtual ~ShellStateConnecting() {} |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
class ShellStateConnected : public ShellState { |
||||||
|
public: |
||||||
|
static ShellStateConnected * getInstance(void) { |
||||||
|
static ShellStateConnected instance; |
||||||
|
return &instance; |
||||||
|
} |
||||||
|
|
||||||
|
virtual void run(ShellContext *); |
||||||
|
|
||||||
|
private: |
||||||
|
ShellStateConnected() {} |
||||||
|
virtual ~ShellStateConnected() {} |
||||||
|
|
||||||
|
void unknownCmd(); |
||||||
|
void handleConError(ShellContext *); |
||||||
|
void helpMsg(); |
||||||
|
}; |
||||||
|
|
||||||
|
#endif |
||||||
|
|
@ -0,0 +1,182 @@ |
|||||||
|
/**
|
||||||
|
* Tests for DBClientProxy class for leveldb |
||||||
|
* @author Bo Liu (newpoo.liu@gmail.com) |
||||||
|
* Copyright 2012 Facebook |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <algorithm> |
||||||
|
#include <vector> |
||||||
|
#include <string> |
||||||
|
#include <protocol/TBinaryProtocol.h> |
||||||
|
#include <transport/TSocket.h> |
||||||
|
#include <transport/TBufferTransports.h> |
||||||
|
#include <util/testharness.h> |
||||||
|
#include <DB.h> |
||||||
|
#include <AssocService.h> |
||||||
|
#include <leveldb_types.h> |
||||||
|
|
||||||
|
#include "server_options.h" |
||||||
|
|
||||||
|
|
||||||
|
#include "../DBClientProxy.h" |
||||||
|
using namespace leveldb; |
||||||
|
|
||||||
|
|
||||||
|
using namespace apache::thrift; |
||||||
|
using namespace apache::thrift::protocol; |
||||||
|
using namespace apache::thrift::transport; |
||||||
|
using boost::shared_ptr; |
||||||
|
using namespace Tleveldb; |
||||||
|
using namespace std; |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
extern "C" void startServer(int argc, char**argv); |
||||||
|
extern "C" void stopServer(int port); |
||||||
|
extern ServerOptions server_options; |
||||||
|
|
||||||
|
static const string db1("db1"); |
||||||
|
|
||||||
|
|
||||||
|
static void testDBClientProxy(DBClientProxy & dbcp) { |
||||||
|
bool flag; |
||||||
|
const int NOK = 100; |
||||||
|
const int BUFSIZE = 16; |
||||||
|
int testcase = 0; |
||||||
|
|
||||||
|
vector<string> keys, values; |
||||||
|
vector<pair<string, string> > kvs, correctKvs; |
||||||
|
string k, v; |
||||||
|
|
||||||
|
for(int i = 0; i < NOK; ++i) { |
||||||
|
char bufKey[BUFSIZE]; |
||||||
|
char bufValue[BUFSIZE]; |
||||||
|
snprintf(bufKey, BUFSIZE, "key%d", i); |
||||||
|
snprintf(bufValue, BUFSIZE, "value%d", i); |
||||||
|
keys.push_back(bufKey); |
||||||
|
values.push_back(bufValue); |
||||||
|
correctKvs.push_back((make_pair(string(bufKey), string(bufValue)))); |
||||||
|
} |
||||||
|
|
||||||
|
sort(correctKvs.begin(), correctKvs.end()); |
||||||
|
|
||||||
|
|
||||||
|
// can not do get(), put(), scan() or create() before connected.
|
||||||
|
flag = dbcp.get(db1, keys[0], v); |
||||||
|
ASSERT_TRUE(false == flag); |
||||||
|
printf("\033[01;40;32mTEST CASE %d passed\033[01;40;37m\n", ++testcase); |
||||||
|
flag = dbcp.put(db1, keys[0], keys[1]); |
||||||
|
ASSERT_TRUE(false == flag); |
||||||
|
printf("\033[01;40;32mTEST CASE %d passed\033[01;40;37m\n", ++testcase); |
||||||
|
flag = dbcp.scan(db1, "a", "w", "100", kvs); |
||||||
|
ASSERT_TRUE(false == flag); |
||||||
|
printf("\033[01;40;32mTEST CASE %d passed\033[01;40;37m\n", ++testcase); |
||||||
|
flag = dbcp.create(db1); |
||||||
|
ASSERT_TRUE(false == flag); |
||||||
|
printf("\033[01;40;32mTEST CASE %d passed\033[01;40;37m\n", ++testcase); |
||||||
|
|
||||||
|
dbcp.connect(); |
||||||
|
|
||||||
|
// create a database
|
||||||
|
flag = dbcp.create(db1); |
||||||
|
ASSERT_TRUE(true == flag); |
||||||
|
printf("\033[01;40;32mTEST CASE %d passed\033[01;40;37m\n", ++testcase); |
||||||
|
|
||||||
|
// no such key
|
||||||
|
flag = dbcp.get(db1, keys[0], v); |
||||||
|
ASSERT_TRUE(false == flag); |
||||||
|
printf("\033[01;40;32mTEST CASE %d passed\033[01;40;37m\n", ++testcase); |
||||||
|
|
||||||
|
|
||||||
|
// scan() success with empty returned key-value pairs
|
||||||
|
kvs.clear(); |
||||||
|
flag = dbcp.scan(db1, "a", "w", "100", kvs); |
||||||
|
ASSERT_TRUE(true == flag); |
||||||
|
ASSERT_TRUE(kvs.empty()); |
||||||
|
printf("\033[01;40;32mTEST CASE %d passed\033[01;40;37m\n", ++testcase); |
||||||
|
|
||||||
|
|
||||||
|
// put()
|
||||||
|
for(int i = 0; i < NOK; ++i) { |
||||||
|
flag = dbcp.put(db1, keys[i], values[i]); |
||||||
|
ASSERT_TRUE(true == flag); |
||||||
|
} |
||||||
|
printf("\033[01;40;32mTEST CASE %d passed\033[01;40;37m\n", ++testcase); |
||||||
|
|
||||||
|
|
||||||
|
// scan all of key-value pairs
|
||||||
|
kvs.clear(); |
||||||
|
flag = dbcp.scan(db1, "a", "w", "100", kvs); |
||||||
|
ASSERT_TRUE(true == flag); |
||||||
|
ASSERT_TRUE(kvs == correctKvs); |
||||||
|
printf("\033[01;40;32mTEST CASE %d passed\033[01;40;37m\n", ++testcase); |
||||||
|
|
||||||
|
|
||||||
|
// scan the first 20 key-value pairs
|
||||||
|
{ |
||||||
|
kvs.clear(); |
||||||
|
flag = dbcp.scan(db1, "a", "w", "20", kvs); |
||||||
|
ASSERT_TRUE(true == flag); |
||||||
|
vector<pair<string, string> > tkvs(correctKvs.begin(), correctKvs.begin() + 20); |
||||||
|
ASSERT_TRUE(kvs == tkvs); |
||||||
|
printf("\033[01;40;32mTEST CASE %d passed\033[01;40;37m\n", ++testcase); |
||||||
|
} |
||||||
|
|
||||||
|
// scan key[10] to key[50]
|
||||||
|
{ |
||||||
|
kvs.clear(); |
||||||
|
flag = dbcp.scan(db1, correctKvs[10].first, correctKvs[50].first, "100", kvs); |
||||||
|
ASSERT_TRUE(true == flag); |
||||||
|
|
||||||
|
vector<pair<string, string> > tkvs(correctKvs.begin() + 10, correctKvs.begin() + 50); |
||||||
|
ASSERT_TRUE(kvs == tkvs); |
||||||
|
printf("\033[01;40;32mTEST CASE %d passed\033[01;40;37m\n", ++testcase); |
||||||
|
} |
||||||
|
|
||||||
|
// scan "key10" to "key40" by limit constraint
|
||||||
|
{ |
||||||
|
kvs.clear(); |
||||||
|
flag = dbcp.scan(db1, correctKvs[10].first.c_str(), "w", "30", kvs); |
||||||
|
ASSERT_TRUE(true == flag); |
||||||
|
vector<pair<string, string> > tkvs(correctKvs.begin() + 10, correctKvs.begin() + 40); |
||||||
|
ASSERT_TRUE(kvs == tkvs); |
||||||
|
printf("\033[01;40;32mTEST CASE %d passed\033[01;40;37m\n", ++testcase); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
// get()
|
||||||
|
flag = dbcp.get(db1, "unknownKey", v); |
||||||
|
ASSERT_TRUE(false == flag); |
||||||
|
printf("\033[01;40;32mTEST CASE %d passed\033[01;40;37m\n", ++testcase); |
||||||
|
|
||||||
|
flag = dbcp.get(db1, keys[0], v); |
||||||
|
ASSERT_TRUE(true == flag); |
||||||
|
ASSERT_TRUE(v == values[0]); |
||||||
|
printf("\033[01;40;32mTEST CASE %d passed\033[01;40;37m\n", ++testcase); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static void cleanupDir(std::string dir) { |
||||||
|
// remove old data, if any
|
||||||
|
char* cleanup = new char[100]; |
||||||
|
snprintf(cleanup, 100, "rm -rf %s", dir.c_str()); |
||||||
|
system(cleanup); |
||||||
|
} |
||||||
|
|
||||||
|
int main(int argc, char **argv) { |
||||||
|
// create a server
|
||||||
|
startServer(argc, argv); |
||||||
|
printf("Server thread created.\n"); |
||||||
|
|
||||||
|
// give some time to the server to initialize itself
|
||||||
|
while (server_options.getPort() == 0) { |
||||||
|
sleep(1); |
||||||
|
} |
||||||
|
|
||||||
|
cleanupDir(server_options.getDataDirectory(db1)); |
||||||
|
|
||||||
|
DBClientProxy dbcp("localhost", server_options.getPort()); |
||||||
|
testDBClientProxy(dbcp); |
||||||
|
} |
||||||
|
|
Loading…
Reference in new issue