Skip to content

Commit

Permalink
WIP: support redis versioning
Browse files Browse the repository at this point in the history
Stores a new key "version" in redis db which contains the git hash
souper was built with (or timestamp if git hash is unavailable). If git
hash of souper matches value of "version" in redis db during connection,
all is good. If it doesn't matches, we error out. If no "version" key
matches, we put current souper version in the redis database and warn
the user that results might be incorrect.
  • Loading branch information
pranavk committed Jan 26, 2019
1 parent 89a83dd commit 4aa2124
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 1 deletion.
46 changes: 46 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -150,13 +150,59 @@ add_library(souperExtractor STATIC
${SOUPER_EXTRACTOR_FILES}
)

# To make sure that we always update the git version hash (even when cmake is not run),
# we make use of a version-tmp.h file that gets updated unconditionally and only gets
# copied to "real" version.h when the content is changed. This will trigger the build
# of other components (dependent on version.h) only when required.
set(VERSION_BARE_FILE version.h)
set(VERSION_BARE_TMP version-tmp.h)
set(VERSION_FILE ${CMAKE_SOURCE_DIR}/include/souper/Util/${VERSION_BARE_FILE})
set(VERSION_TMP ${CMAKE_SOURCE_DIR}/include/souper/Util/${VERSION_BARE_TMP})
find_package(Git)
if (Git_FOUND AND (EXISTS ${CMAKE_SOURCE_DIR}/.git))
add_custom_command(
OUTPUT ${VERSION_TMP}
COMMAND ${CMAKE_COMMAND} -E echo "#ifndef SOUPER_VERSION_H" > ${VERSION_TMP}
COMMAND ${CMAKE_COMMAND} -E echo "#define SOUPER_VERSION_H" >> ${VERSION_TMP}
COMMAND ${CMAKE_COMMAND} -E echo "" >> ${VERSION_TMP}
COMMAND ${CMAKE_COMMAND} -E echo "#define STR_VALX(a) #a" >> ${VERSION_TMP}
COMMAND ${CMAKE_COMMAND} -E echo "#define STR_VAL(a) STR_VALX(a)" >> ${VERSION_TMP}
COMMAND ${CMAKE_COMMAND} -E echo_append "#define SOUPER_VERSION_HASH_RAW " >> ${VERSION_TMP}
COMMAND ${GIT_EXECUTABLE} describe --always --dirty >> ${VERSION_TMP}
COMMAND ${CMAKE_COMMAND} -E echo "#define SOUPER_VERSION_HASH STR_VAL(SOUPER_VERSION_HASH_RAW)" >> ${VERSION_TMP}
COMMAND ${CMAKE_COMMAND} -E echo "" >> ${VERSION_TMP}
COMMAND ${CMAKE_COMMAND} -E echo "#endif" >> ${VERSION_TMP}
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${VERSION_TMP} ${VERSION_FILE}
COMMAND ${CMAKE_COMMAND} -E remove ${VERSION_TMP}
VERBATIM)
else()
# If there's no way to get the git hash, fallback to timestamp when file was compiled.
# This has the disadvantage that it will always trigger build of components that are
# dependent on version.h
add_custom_command(
OUTPUT ${VERSION_TMP}
COMMAND ${CMAKE_COMMAND} -E echo "#ifndef SOUPER_VERSION_H" > ${VERSION_TMP}
COMMAND ${CMAKE_COMMAND} -E echo "#define SOUPER_VERSION_H" >> ${VERSION_TMP}
COMMAND ${CMAKE_COMMAND} -E echo "" >> ${VERSION_TMP}
COMMAND ${CMAKE_COMMAND} -E echo "" >> ${VERSION_TMP}
COMMAND ${CMAKE_COMMAND} -E echo "#define SOUPER_VERSION_HASH __DATE__ \" \" __TIME__" >> ${VERSION_TMP}
COMMAND ${CMAKE_COMMAND} -E echo "" >> ${VERSION_TMP}
COMMAND ${CMAKE_COMMAND} -E echo "#endif" >> ${VERSION_TMP}
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${VERSION_TMP} ${VERSION_FILE}
COMMAND ${CMAKE_COMMAND} -E remove ${VERSION_TMP}
VERBATIM)
endif()

set(SOUPER_KVSTORE_FILES
lib/KVStore/KVStore.cpp
include/souper/KVStore/KVStore.h
)

# All those components requiring version information need to mention {VERSION_TMP} as their
# dependent.
add_library(souperKVStore STATIC
${SOUPER_KVSTORE_FILES}
${VERSION_TMP}
)

set(SOUPER_INFER_FILES
Expand Down
50 changes: 49 additions & 1 deletion lib/KVStore/KVStore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
#include "llvm/Support/CommandLine.h"
#include "hiredis.h"

#include "souper/Util/version.h"

using namespace llvm;
using namespace souper;

Expand All @@ -26,7 +28,12 @@ static cl::opt<unsigned> RedisPort("souper-redis-port", cl::init(6379),
namespace souper {

class KVStore::KVImpl {
redisContext *Ctx;
redisContext *Ctx = 0;

private:
// checks if current redis database is compatible with current version of souper
bool checkCompatibility();

public:
KVImpl();
~KVImpl();
Expand All @@ -46,12 +53,53 @@ KVStore::KVImpl::KVImpl() {
llvm::report_fatal_error((llvm::StringRef)"Redis connection error: " +
Ctx->errstr + "\n");
}

if (!checkCompatibility()) {
llvm::report_fatal_error("Redis database on port %d is incompatible.", RedisPort);
}
}

KVStore::KVImpl::~KVImpl() {
redisFree(Ctx);
}

bool KVStore::KVImpl::checkCompatibility() {
assert(Ctx && "Cannot check compatibility on an uninitialized database.");

redisReply *reply = static_cast<redisReply*>(redisCommand(Ctx, "GET version"));
if (!reply || Ctx->err) {
llvm::report_fatal_error((llvm::StringRef)"Redis error: " + Ctx->errstr);
}

bool compat = true;
switch(reply->type) {
case REDIS_REPLY_NIL:
// no version set
errs() << "Unversioned redis database found. Assigning redis version to current souper version. Results may be incorrect.\n";

freeReplyObject(reply);
reply = static_cast<redisReply*>(redisCommand(Ctx, "SET version %s", SOUPER_VERSION_HASH));
// TODO: Factor out all such snippets
if (!reply || Ctx->err) {
llvm::report_fatal_error((llvm::StringRef)"Redis error: " + Ctx->errstr);
}
break;
case REDIS_REPLY_STRING:
{
llvm::StringRef value = reply->str;
if (value != SOUPER_VERSION_HASH) {
errs() << "Incompatible DB: Redis version: " << value << "; current souper version: " << SOUPER_VERSION_HASH << "\n";
compat = false;
}
}
break;
default:
compat = false;
}

return compat;
}

void KVStore::KVImpl::hIncrBy(llvm::StringRef Key, llvm::StringRef Field,
int Incr) {
redisReply *reply = (redisReply *)redisCommand(Ctx, "HINCRBY %s %s 1",
Expand Down

0 comments on commit 4aa2124

Please sign in to comment.