Summary: Closes https://github.com/facebook/rocksdb/pull/2589 Differential Revision: D5431502 Pulled By: siying fbshipit-source-id: 8ebf8c87883daa9daa54b2303d11ce01ab1f6f75
243 lines
7.7 KiB
C++
243 lines
7.7 KiB
C++
// Copyright (c) 2016, Facebook, Inc. All rights reserved.
|
|
// This source code is licensed under both the GPLv2 (found in the
|
|
// COPYING file in the root directory) and Apache 2.0 License
|
|
// (found in the LICENSE.Apache file in the root directory).
|
|
|
|
#if defined(LUA) && !defined(ROCKSDB_LITE)
|
|
#include "rocksdb/utilities/lua/rocks_lua_compaction_filter.h"
|
|
|
|
extern "C" {
|
|
#include <luaconf.h>
|
|
}
|
|
|
|
#include "rocksdb/compaction_filter.h"
|
|
|
|
namespace rocksdb {
|
|
namespace lua {
|
|
|
|
const std::string kFilterFunctionName = "Filter";
|
|
const std::string kNameFunctionName = "Name";
|
|
|
|
void RocksLuaCompactionFilter::LogLuaError(const char* format, ...) const {
|
|
if (options_.error_log.get() != nullptr &&
|
|
error_count_ < options_.error_limit_per_filter) {
|
|
error_count_++;
|
|
|
|
va_list ap;
|
|
va_start(ap, format);
|
|
options_.error_log->Logv(InfoLogLevel::ERROR_LEVEL, format, ap);
|
|
va_end(ap);
|
|
}
|
|
}
|
|
|
|
bool RocksLuaCompactionFilter::Filter(int level, const Slice& key,
|
|
const Slice& existing_value,
|
|
std::string* new_value,
|
|
bool* value_changed) const {
|
|
auto* lua_state = lua_state_wrapper_.GetLuaState();
|
|
// push the right function into the lua stack
|
|
lua_getglobal(lua_state, kFilterFunctionName.c_str());
|
|
|
|
int error_no = 0;
|
|
int num_input_values;
|
|
int num_return_values;
|
|
if (options_.ignore_value == false) {
|
|
// push input arguments into the lua stack
|
|
lua_pushnumber(lua_state, level);
|
|
lua_pushlstring(lua_state, key.data(), key.size());
|
|
lua_pushlstring(lua_state, existing_value.data(), existing_value.size());
|
|
num_input_values = 3;
|
|
num_return_values = 3;
|
|
} else {
|
|
// If ignore_value is set to true, then we only put two arguments
|
|
// and expect one return value
|
|
lua_pushnumber(lua_state, level);
|
|
lua_pushlstring(lua_state, key.data(), key.size());
|
|
num_input_values = 2;
|
|
num_return_values = 1;
|
|
}
|
|
|
|
// perform the lua call
|
|
if ((error_no =
|
|
lua_pcall(lua_state, num_input_values, num_return_values, 0)) != 0) {
|
|
LogLuaError("[Lua] Error(%d) in Filter function --- %s", error_no,
|
|
lua_tostring(lua_state, -1));
|
|
// pops out the lua error from stack
|
|
lua_pop(lua_state, 1);
|
|
return false;
|
|
}
|
|
|
|
// As lua_pcall went successfully, it can be guaranteed that the top
|
|
// three elements in the Lua stack are the three returned values.
|
|
|
|
bool has_error = false;
|
|
const int kIndexIsFiltered = -num_return_values;
|
|
const int kIndexValueChanged = -num_return_values + 1;
|
|
const int kIndexNewValue = -num_return_values + 2;
|
|
|
|
// check the types of three return values
|
|
// is_filtered
|
|
if (!lua_isboolean(lua_state, kIndexIsFiltered)) {
|
|
LogLuaError(
|
|
"[Lua] Error in Filter function -- "
|
|
"1st return value (is_filtered) is not a boolean "
|
|
"while a boolean is expected.");
|
|
has_error = true;
|
|
}
|
|
|
|
if (options_.ignore_value == false) {
|
|
// value_changed
|
|
if (!lua_isboolean(lua_state, kIndexValueChanged)) {
|
|
LogLuaError(
|
|
"[Lua] Error in Filter function -- "
|
|
"2nd return value (value_changed) is not a boolean "
|
|
"while a boolean is expected.");
|
|
has_error = true;
|
|
}
|
|
// new_value
|
|
if (!lua_isstring(lua_state, kIndexNewValue)) {
|
|
LogLuaError(
|
|
"[Lua] Error in Filter function -- "
|
|
"3rd return value (new_value) is not a string "
|
|
"while a string is expected.");
|
|
has_error = true;
|
|
}
|
|
}
|
|
|
|
if (has_error) {
|
|
lua_pop(lua_state, num_return_values);
|
|
return false;
|
|
}
|
|
|
|
// Fetch the return values
|
|
bool is_filtered = false;
|
|
if (!has_error) {
|
|
is_filtered = lua_toboolean(lua_state, kIndexIsFiltered);
|
|
if (options_.ignore_value == false) {
|
|
*value_changed = lua_toboolean(lua_state, kIndexValueChanged);
|
|
if (*value_changed) {
|
|
const char* new_value_buf = lua_tostring(lua_state, kIndexNewValue);
|
|
const size_t new_value_size = lua_strlen(lua_state, kIndexNewValue);
|
|
// Note that any string that lua_tostring returns always has a zero at
|
|
// its end, bu/t it can have other zeros inside it
|
|
assert(new_value_buf[new_value_size] == '\0');
|
|
assert(strlen(new_value_buf) <= new_value_size);
|
|
new_value->assign(new_value_buf, new_value_size);
|
|
}
|
|
} else {
|
|
*value_changed = false;
|
|
}
|
|
}
|
|
// pops the three return values.
|
|
lua_pop(lua_state, num_return_values);
|
|
return is_filtered;
|
|
}
|
|
|
|
const char* RocksLuaCompactionFilter::Name() const {
|
|
if (name_ != "") {
|
|
return name_.c_str();
|
|
}
|
|
auto* lua_state = lua_state_wrapper_.GetLuaState();
|
|
// push the right function into the lua stack
|
|
lua_getglobal(lua_state, kNameFunctionName.c_str());
|
|
|
|
// perform the call (0 arguments, 1 result)
|
|
int error_no;
|
|
if ((error_no = lua_pcall(lua_state, 0, 1, 0)) != 0) {
|
|
LogLuaError("[Lua] Error(%d) in Name function --- %s", error_no,
|
|
lua_tostring(lua_state, -1));
|
|
// pops out the lua error from stack
|
|
lua_pop(lua_state, 1);
|
|
return name_.c_str();
|
|
}
|
|
|
|
// check the return value
|
|
if (!lua_isstring(lua_state, -1)) {
|
|
LogLuaError(
|
|
"[Lua] Error in Name function -- "
|
|
"return value is not a string while string is expected");
|
|
} else {
|
|
const char* name_buf = lua_tostring(lua_state, -1);
|
|
const size_t name_size __attribute__((unused)) = lua_strlen(lua_state, -1);
|
|
assert(name_buf[name_size] == '\0');
|
|
assert(strlen(name_buf) <= name_size);
|
|
name_ = name_buf;
|
|
}
|
|
lua_pop(lua_state, 1);
|
|
return name_.c_str();
|
|
}
|
|
|
|
/* Not yet supported
|
|
bool RocksLuaCompactionFilter::FilterMergeOperand(
|
|
int level, const Slice& key, const Slice& operand) const {
|
|
auto* lua_state = lua_state_wrapper_.GetLuaState();
|
|
// push the right function into the lua stack
|
|
lua_getglobal(lua_state, "FilterMergeOperand");
|
|
|
|
// push input arguments into the lua stack
|
|
lua_pushnumber(lua_state, level);
|
|
lua_pushlstring(lua_state, key.data(), key.size());
|
|
lua_pushlstring(lua_state, operand.data(), operand.size());
|
|
|
|
// perform the call (3 arguments, 1 result)
|
|
int error_no;
|
|
if ((error_no = lua_pcall(lua_state, 3, 1, 0)) != 0) {
|
|
LogLuaError("[Lua] Error(%d) in FilterMergeOperand function --- %s",
|
|
error_no, lua_tostring(lua_state, -1));
|
|
// pops out the lua error from stack
|
|
lua_pop(lua_state, 1);
|
|
return false;
|
|
}
|
|
|
|
bool is_filtered = false;
|
|
// check the return value
|
|
if (!lua_isboolean(lua_state, -1)) {
|
|
LogLuaError("[Lua] Error in FilterMergeOperand function -- "
|
|
"return value is not a boolean while boolean is expected");
|
|
} else {
|
|
is_filtered = lua_toboolean(lua_state, -1);
|
|
}
|
|
|
|
lua_pop(lua_state, 1);
|
|
|
|
return is_filtered;
|
|
}
|
|
*/
|
|
|
|
bool RocksLuaCompactionFilter::IgnoreSnapshots() const {
|
|
return options_.ignore_snapshots;
|
|
}
|
|
|
|
RocksLuaCompactionFilterFactory::RocksLuaCompactionFilterFactory(
|
|
const RocksLuaCompactionFilterOptions opt)
|
|
: opt_(opt) {
|
|
auto filter = CreateCompactionFilter(CompactionFilter::Context());
|
|
name_ = std::string("RocksLuaCompactionFilterFactory::") +
|
|
std::string(filter->Name());
|
|
}
|
|
|
|
std::unique_ptr<CompactionFilter>
|
|
RocksLuaCompactionFilterFactory::CreateCompactionFilter(
|
|
const CompactionFilter::Context& context) {
|
|
std::lock_guard<std::mutex> lock(opt_mutex_);
|
|
return std::unique_ptr<CompactionFilter>(new RocksLuaCompactionFilter(opt_));
|
|
}
|
|
|
|
std::string RocksLuaCompactionFilterFactory::GetScript() {
|
|
std::lock_guard<std::mutex> lock(opt_mutex_);
|
|
return opt_.lua_script;
|
|
}
|
|
|
|
void RocksLuaCompactionFilterFactory::SetScript(const std::string& new_script) {
|
|
std::lock_guard<std::mutex> lock(opt_mutex_);
|
|
opt_.lua_script = new_script;
|
|
}
|
|
|
|
const char* RocksLuaCompactionFilterFactory::Name() const {
|
|
return name_.c_str();
|
|
}
|
|
|
|
} // namespace lua
|
|
} // namespace rocksdb
|
|
#endif // defined(LUA) && !defined(ROCKSDB_LITE)
|