Add redis database backend (port of sfan5's patches)

Patches included/ported:
	2553e44e8dec75d99408b56f469ff4be61c84336
	79d338a90c1a99fce9334bea5d4c296f190373fc
This commit is contained in:
Rogier 2014-05-12 12:17:49 +02:00
parent 98fa76bb6b
commit d707b882e8
7 changed files with 242 additions and 4 deletions

View File

@ -119,6 +119,26 @@ if(ENABLE_LEVELDB)
endif(LEVELDB_LIBRARY AND LEVELDB_INCLUDE_DIR) endif(LEVELDB_LIBRARY AND LEVELDB_INCLUDE_DIR)
endif(ENABLE_LEVELDB) endif(ENABLE_LEVELDB)
# Find redis
set(USE_REDIS 0)
OPTION(ENABLE_REDIS "Enable redis backend")
if(ENABLE_REDIS)
find_library(REDIS_LIBRARY hiredis)
find_path(REDIS_INCLUDE_DIR hiredis.h PATH_SUFFIXES hiredis)
message (STATUS "redis library: ${REDIS_LIBRARY}")
message (STATUS "redis headers: ${REDIS_INCLUDE_DIR}")
if(REDIS_LIBRARY AND REDIS_INCLUDE_DIR)
set(USE_REDIS 1)
message(STATUS "redis backend enabled")
include_directories(${REDIS_INCLUDE_DIR})
else(REDIS_LIBRARY AND REDIS_INCLUDE_DIR)
set(USE_REDIS 0)
message(STATUS "redis not found!")
endif(REDIS_LIBRARY AND REDIS_INCLUDE_DIR)
endif(ENABLE_REDIS)
include_directories( include_directories(
"${PROJECT_BINARY_DIR}" "${PROJECT_BINARY_DIR}"
"${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}"
@ -148,6 +168,10 @@ if(USE_LEVELDB)
set(mapper_SRCS ${mapper_SRCS} db-leveldb.cpp) set(mapper_SRCS ${mapper_SRCS} db-leveldb.cpp)
endif(USE_LEVELDB) endif(USE_LEVELDB)
if(USE_REDIS)
set(mapper_SRCS ${mapper_SRCS} db-redis.cpp)
endif(USE_REDIS)
add_executable(minetestmapper add_executable(minetestmapper
${mapper_SRCS} ${mapper_SRCS}
) )
@ -156,6 +180,7 @@ target_link_libraries(
minetestmapper minetestmapper
${SQLITE3_LIBRARY} ${SQLITE3_LIBRARY}
${LEVELDB_LIBRARY} ${LEVELDB_LIBRARY}
${REDIS_LIBRARY}
${LIBGD_LIBRARY} ${LIBGD_LIBRARY}
${ZLIB_LIBRARY} ${ZLIB_LIBRARY}
) )

View File

@ -9,6 +9,7 @@ Requirements
* libgd * libgd
* sqlite3 * sqlite3
* leveldb (optional, set ENABLE_LEVELDB=1 in CMake to enable leveldb support) * leveldb (optional, set ENABLE_LEVELDB=1 in CMake to enable leveldb support)
* hiredis (optional, set ENABLE_REDIS=1 in CMake to enable redis support)
Compilation Compilation
----------- -----------
@ -20,11 +21,11 @@ Plain:
cmake . cmake .
make make
With levelDB support: With levelDB and Redis support:
:: ::
cmake -DENABLE_LEVELDB=true . cmake -DENABLE_LEVELDB=true -DENABLE_REDIS=true .
make make
Debug version: Debug version:
@ -122,7 +123,7 @@ max-y:
Don't draw nodes above this y value, `--max-y 75` Don't draw nodes above this y value, `--max-y 75`
backend: backend:
Use specific map backend, supported: sqlite3, leveldb, `--backend leveldb` Use specific map backend, supported: sqlite3, leveldb, redis, `--backend leveldb`
geometry <geometry>: geometry <geometry>:
(see below, under 'centergeometry') (see below, under 'centergeometry')

View File

@ -26,6 +26,9 @@
#if USE_LEVELDB #if USE_LEVELDB
#include "db-leveldb.h" #include "db-leveldb.h"
#endif #endif
#if USE_REDIS
#include "db-redis.h"
#endif
using namespace std; using namespace std;
@ -383,6 +386,10 @@ void TileGenerator::openDb(const std::string &input)
#if USE_LEVELDB #if USE_LEVELDB
else if(m_backend == "leveldb") else if(m_backend == "leveldb")
m_db = new DBLevelDB(input); m_db = new DBLevelDB(input);
#endif
#if USE_REDIS
else if(m_backend == "redis")
m_db = new DBRedis(input);
#endif #endif
else else
throw std::runtime_error(((std::string) "Unknown map backend: ") + m_backend); throw std::runtime_error(((std::string) "Unknown map backend: ") + m_backend);

View File

@ -4,6 +4,7 @@
#define CMAKE_CONFIG_H #define CMAKE_CONFIG_H
#define USE_LEVELDB @USE_LEVELDB@ #define USE_LEVELDB @USE_LEVELDB@
#define USE_REDIS @USE_REDIS@
#endif #endif

180
db-redis.cpp Normal file
View File

@ -0,0 +1,180 @@
#include <stdexcept>
#include <sstream>
#include <fstream>
#include "db-redis.h"
#include "types.h"
static inline int64_t stoi64(const std::string &s)
{
std::stringstream tmp(s);
int64_t t;
tmp >> t;
return t;
}
static inline std::string i64tos(int64_t i)
{
std::ostringstream os;
os << i;
return os.str();
}
inline std::string trim(const std::string &s)
{
size_t front = 0;
while(s[front] == ' ' ||
s[front] == '\t' ||
s[front] == '\r' ||
s[front] == '\n'
)
++front;
size_t back = s.size();
while(back > front &&
(s[back-1] == ' ' ||
s[back-1] == '\t' ||
s[back-1] == '\r' ||
s[back-1] == '\n'
)
)
--back;
return s.substr(front, back - front);
}
#define EOFCHECK() \
if(is.eof()) \
throw std::runtime_error("setting not found");
std::string get_setting(std::string name, std::istream &is)
{
char c;
char s[256];
std::string nm, value;
next:
while((c = is.get()) == ' ' || c == '\t' || c == '\r' || c == '\n')
;
EOFCHECK();
if(c == '#') // Ignore comments
is.ignore(0xffff, '\n');
EOFCHECK();
s[0] = c; // The current char belongs to the name too
is.get(&s[1], 255, '=');
is.ignore(1); // Jump over the =
EOFCHECK();
nm = trim(std::string(s));
is.get(s, 256, '\n');
value = trim(std::string(s));
if(name == nm)
return value;
else
goto next;
}
#undef EOFCHECK
std::string get_setting_default(std::string name, std::istream &is, const std::string def)
{
try {
return get_setting(name, is);
} catch(std::runtime_error e) {
return def;
}
}
DBRedis::DBRedis(const std::string &mapdir) :
m_blocksReadCount(0),
m_blocksUnCachedCount(0)
{
std::ifstream ifs((mapdir + "/world.mt").c_str());
if(!ifs.good())
throw std::runtime_error("Failed to read world.mt");
std::string tmp;
try {
tmp = get_setting("redis_address", ifs);
ifs.seekg(0);
hash = get_setting("redis_hash", ifs);
ifs.seekg(0);
} catch(std::runtime_error e) {
throw std::runtime_error("Set redis_address and redis_hash in world.mt to use the redis backend");
}
const char *addr = tmp.c_str();
int port = stoi64(get_setting_default("redis_port", ifs, "6379"));
ctx = redisConnect(addr, port);
if(!ctx)
throw std::runtime_error("Cannot allocate redis context");
else if(ctx->err) {
std::string err = std::string("Connection error: ") + ctx->errstr;
redisFree(ctx);
throw std::runtime_error(err);
}
}
DBRedis::~DBRedis()
{
redisFree(ctx);
}
int DBRedis::getBlocksReadCount(void)
{
return m_blocksReadCount;
}
int DBRedis::getBlocksCachedCount(void)
{
return 0;
}
int DBRedis::getBlocksUnCachedCount(void)
{
return m_blocksUnCachedCount;
}
const DB::BlockPosList &DBRedis::getBlockPos()
{
redisReply *reply;
reply = (redisReply*) redisCommand(ctx, "HKEYS %s", hash.c_str());
if(!reply)
throw std::runtime_error(std::string("redis command 'HKEYS %s' failed: ") + ctx->errstr);
if(reply->type != REDIS_REPLY_ARRAY)
throw std::runtime_error("Failed to get keys from database");
for(size_t i = 0; i < reply->elements; i++) {
if(!reply->element[i]->type == REDIS_REPLY_STRING)
throw std::runtime_error("Got wrong response to 'HKEYS %s' command");
m_blockPosList.push_back(stoi64(reply->element[i]->str));
}
freeReplyObject(reply);
return m_blockPosList;
}
DB::Block DBRedis::getBlockOnPos(const BlockPos &pos)
{
redisReply *reply;
std::string tmp;
Block block(pos,reinterpret_cast<const unsigned char *>(""));
m_blocksReadCount++;
tmp = i64tos(pos.databasePos());
reply = (redisReply*) redisCommand(ctx, "HGET %s %s", hash.c_str(), tmp.c_str());
if(!reply)
throw std::runtime_error(std::string("redis command 'HGET %s %s' failed: ") + ctx->errstr);
if (reply->type == REDIS_REPLY_STRING && reply->len != 0) {
m_blocksUnCachedCount++;
block = Block(pos, ustring(reinterpret_cast<const unsigned char *>(reply->str), reply->len));
} else
throw std::runtime_error("Got wrong response to 'HGET %s %s' command");
freeReplyObject(reply);
return block;
}

24
db-redis.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef DB_REDIS_HEADER
#define DB_REDIS_HEADER
#include "db.h"
#include <hiredis.h>
class DBRedis : public DB {
public:
DBRedis(const std::string &mapdir);
virtual int getBlocksUnCachedCount(void);
virtual int getBlocksCachedCount(void);
virtual int getBlocksReadCount(void);
virtual const BlockPosList &getBlockPos();
virtual Block getBlockOnPos(const BlockPos &pos);
~DBRedis();
private:
int m_blocksReadCount;
int m_blocksUnCachedCount;
redisContext *ctx;
std::string hash;
BlockPosList m_blockPosList;
};
#endif // DB_REDIS_HEADER

View File

@ -44,7 +44,7 @@ void usage()
" --noshading\n" " --noshading\n"
" --min-y <y>\n" " --min-y <y>\n"
" --max-y <y>\n" " --max-y <y>\n"
" --backend <sqlite3/leveldb>\n" " --backend <sqlite3/leveldb/redis>\n"
" --geometry <geometry>\n" " --geometry <geometry>\n"
" --cornergeometry <geometry>\n" " --cornergeometry <geometry>\n"
" --centergeometry <geometry>\n" " --centergeometry <geometry>\n"