Trigger restore after query modules load (#183)

This commit is contained in:
antonio2368 2021-06-30 10:01:51 +02:00 committed by GitHub
parent 15911b64dc
commit e016c74e4b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 47 additions and 29 deletions

View File

@ -1069,6 +1069,15 @@ int main(int argc, char **argv) {
query::procedure::gModuleRegistry.SetModulesDirectory(query_modules_directories); query::procedure::gModuleRegistry.SetModulesDirectory(query_modules_directories);
query::procedure::gModuleRegistry.UnloadAndLoadModulesFromDirectories(); query::procedure::gModuleRegistry.UnloadAndLoadModulesFromDirectories();
{
// Triggers can execute query procedures, so we need to reload the modules first and then
// the triggers
auto storage_accessor = interpreter_context.db->Access();
auto dba = query::DbAccessor{&storage_accessor};
interpreter_context.trigger_store.RestoreTriggers(&interpreter_context.ast_cache, &dba,
&interpreter_context.antlr_lock);
}
#ifdef MG_ENTERPRISE #ifdef MG_ENTERPRISE
AuthQueryHandler auth_handler(&auth, std::regex(FLAGS_auth_user_or_role_name_regex)); AuthQueryHandler auth_handler(&auth, std::regex(FLAGS_auth_user_or_role_name_regex));
#else #else

View File

@ -602,11 +602,8 @@ std::optional<plan::ProfilingStatsWithTotalTime> PullPlan::Pull(AnyStream *strea
using RWType = plan::ReadWriteTypeChecker::RWType; using RWType = plan::ReadWriteTypeChecker::RWType;
} // namespace } // namespace
InterpreterContext::InterpreterContext(storage::Storage *db, const std::filesystem::path &data_directory) : db(db) { InterpreterContext::InterpreterContext(storage::Storage *db, const std::filesystem::path &data_directory)
auto storage_accessor = db->Access(); : db(db), trigger_store(data_directory / "triggers") {}
DbAccessor dba{&storage_accessor};
trigger_store.emplace(data_directory / "triggers", &ast_cache, &dba, &antlr_lock);
}
Interpreter::Interpreter(InterpreterContext *interpreter_context) : interpreter_context_(interpreter_context) { Interpreter::Interpreter(InterpreterContext *interpreter_context) : interpreter_context_(interpreter_context) {
MG_ASSERT(interpreter_context_, "Interpreter context must not be NULL"); MG_ASSERT(interpreter_context_, "Interpreter context must not be NULL");
@ -627,8 +624,8 @@ PreparedQuery Interpreter::PrepareTransactionQuery(std::string_view query_upper)
std::make_unique<storage::Storage::Accessor>(interpreter_context_->db->Access(GetIsolationLevelOverride())); std::make_unique<storage::Storage::Accessor>(interpreter_context_->db->Access(GetIsolationLevelOverride()));
execution_db_accessor_.emplace(db_accessor_.get()); execution_db_accessor_.emplace(db_accessor_.get());
if (interpreter_context_->trigger_store->HasTriggers()) { if (interpreter_context_->trigger_store.HasTriggers()) {
trigger_context_collector_.emplace(interpreter_context_->trigger_store->GetEventTypes()); trigger_context_collector_.emplace(interpreter_context_->trigger_store.GetEventTypes());
} }
}; };
} else if (query_upper == "COMMIT") { } else if (query_upper == "COMMIT") {
@ -1090,7 +1087,7 @@ Callback CreateTrigger(TriggerQuery *trigger_query,
[trigger_name = std::move(trigger_query->trigger_name_), trigger_statement = std::move(trigger_query->statement_), [trigger_name = std::move(trigger_query->trigger_name_), trigger_statement = std::move(trigger_query->statement_),
event_type = trigger_query->event_type_, before_commit = trigger_query->before_commit_, interpreter_context, dba, event_type = trigger_query->event_type_, before_commit = trigger_query->before_commit_, interpreter_context, dba,
user_parameters]() -> std::vector<std::vector<TypedValue>> { user_parameters]() -> std::vector<std::vector<TypedValue>> {
interpreter_context->trigger_store->AddTrigger( interpreter_context->trigger_store.AddTrigger(
trigger_name, trigger_statement, user_parameters, ToTriggerEventType(event_type), trigger_name, trigger_statement, user_parameters, ToTriggerEventType(event_type),
before_commit ? TriggerPhase::BEFORE_COMMIT : TriggerPhase::AFTER_COMMIT, &interpreter_context->ast_cache, before_commit ? TriggerPhase::BEFORE_COMMIT : TriggerPhase::AFTER_COMMIT, &interpreter_context->ast_cache,
dba, &interpreter_context->antlr_lock); dba, &interpreter_context->antlr_lock);
@ -1102,7 +1099,7 @@ Callback DropTrigger(TriggerQuery *trigger_query, InterpreterContext *interprete
return {{}, return {{},
[trigger_name = std::move(trigger_query->trigger_name_), [trigger_name = std::move(trigger_query->trigger_name_),
interpreter_context]() -> std::vector<std::vector<TypedValue>> { interpreter_context]() -> std::vector<std::vector<TypedValue>> {
interpreter_context->trigger_store->DropTrigger(trigger_name); interpreter_context->trigger_store.DropTrigger(trigger_name);
return {}; return {};
}}; }};
} }
@ -1110,7 +1107,7 @@ Callback DropTrigger(TriggerQuery *trigger_query, InterpreterContext *interprete
Callback ShowTriggers(InterpreterContext *interpreter_context) { Callback ShowTriggers(InterpreterContext *interpreter_context) {
return {{"trigger name", "statement", "event type", "phase"}, [interpreter_context] { return {{"trigger name", "statement", "event type", "phase"}, [interpreter_context] {
std::vector<std::vector<TypedValue>> results; std::vector<std::vector<TypedValue>> results;
auto trigger_infos = interpreter_context->trigger_store->GetTriggerInfo(); auto trigger_infos = interpreter_context->trigger_store.GetTriggerInfo();
results.reserve(trigger_infos.size()); results.reserve(trigger_infos.size());
for (auto &trigger_info : trigger_infos) { for (auto &trigger_info : trigger_infos) {
std::vector<TypedValue> typed_trigger_info; std::vector<TypedValue> typed_trigger_info;
@ -1503,8 +1500,8 @@ Interpreter::PrepareResult Interpreter::Prepare(const std::string &query_string,
std::make_unique<storage::Storage::Accessor>(interpreter_context_->db->Access(GetIsolationLevelOverride())); std::make_unique<storage::Storage::Accessor>(interpreter_context_->db->Access(GetIsolationLevelOverride()));
execution_db_accessor_.emplace(db_accessor_.get()); execution_db_accessor_.emplace(db_accessor_.get());
if (utils::Downcast<CypherQuery>(parsed_query.query) && interpreter_context_->trigger_store->HasTriggers()) { if (utils::Downcast<CypherQuery>(parsed_query.query) && interpreter_context_->trigger_store.HasTriggers()) {
trigger_context_collector_.emplace(interpreter_context_->trigger_store->GetEventTypes()); trigger_context_collector_.emplace(interpreter_context_->trigger_store.GetEventTypes());
} }
} }
@ -1655,7 +1652,7 @@ void Interpreter::Commit() {
if (trigger_context) { if (trigger_context) {
// Run the triggers // Run the triggers
for (const auto &trigger : interpreter_context_->trigger_store->BeforeCommitTriggers().access()) { for (const auto &trigger : interpreter_context_->trigger_store.BeforeCommitTriggers().access()) {
utils::MonotonicBufferResource execution_memory{kExecutionMemoryBlockSize}; utils::MonotonicBufferResource execution_memory{kExecutionMemoryBlockSize};
AdvanceCommand(); AdvanceCommand();
try { try {
@ -1707,11 +1704,11 @@ void Interpreter::Commit() {
// probably will schedule its after commit triggers, because the other transactions that want to commit are still // probably will schedule its after commit triggers, because the other transactions that want to commit are still
// waiting for commiting or one of them just started commiting its changes. // waiting for commiting or one of them just started commiting its changes.
// This means the ordered execution of after commit triggers are not guaranteed. // This means the ordered execution of after commit triggers are not guaranteed.
if (trigger_context && interpreter_context_->trigger_store->AfterCommitTriggers().size() > 0) { if (trigger_context && interpreter_context_->trigger_store.AfterCommitTriggers().size() > 0) {
interpreter_context_->after_commit_trigger_pool.AddTask( interpreter_context_->after_commit_trigger_pool.AddTask(
[trigger_context = std::move(*trigger_context), interpreter_context = this->interpreter_context_, [trigger_context = std::move(*trigger_context), interpreter_context = this->interpreter_context_,
user_transaction = std::shared_ptr(std::move(db_accessor_))]() mutable { user_transaction = std::shared_ptr(std::move(db_accessor_))]() mutable {
RunTriggersIndividually(interpreter_context->trigger_store->AfterCommitTriggers(), interpreter_context, RunTriggersIndividually(interpreter_context->trigger_store.AfterCommitTriggers(), interpreter_context,
std::move(trigger_context)); std::move(trigger_context));
user_transaction->FinalizeTransaction(); user_transaction->FinalizeTransaction();
SPDLOG_DEBUG("Finished executing after commit triggers"); // NOLINT(bugprone-lambda-function-name) SPDLOG_DEBUG("Finished executing after commit triggers"); // NOLINT(bugprone-lambda-function-name)

View File

@ -169,7 +169,7 @@ struct InterpreterContext {
utils::SkipList<QueryCacheEntry> ast_cache; utils::SkipList<QueryCacheEntry> ast_cache;
utils::SkipList<PlanCacheEntry> plan_cache; utils::SkipList<PlanCacheEntry> plan_cache;
std::optional<TriggerStore> trigger_store; TriggerStore trigger_store;
utils::ThreadPool after_commit_trigger_pool{1}; utils::ThreadPool after_commit_trigger_pool{1};
}; };

View File

@ -230,12 +230,16 @@ void Trigger::Execute(DbAccessor *dba, utils::MonotonicBufferResource *execution
} }
namespace { namespace {
// When the format of the persisted trigger is changed, increase this version
constexpr uint64_t kVersion{1}; constexpr uint64_t kVersion{1};
} // namespace } // namespace
TriggerStore::TriggerStore(std::filesystem::path directory, utils::SkipList<QueryCacheEntry> *query_cache, TriggerStore::TriggerStore(std::filesystem::path directory) : storage_{std::move(directory)} {}
DbAccessor *db_accessor, utils::SpinLock *antlr_lock)
: storage_{std::move(directory)} { void TriggerStore::RestoreTriggers(utils::SkipList<QueryCacheEntry> *query_cache, DbAccessor *db_accessor,
utils::SpinLock *antlr_lock) {
MG_ASSERT(before_commit_triggers_.size() == 0 && after_commit_triggers_.size() == 0,
"Cannot restore trigger when some triggers already exist!");
spdlog::info("Loading triggers..."); spdlog::info("Loading triggers...");
for (const auto &[trigger_name, trigger_data] : storage_) { for (const auto &[trigger_name, trigger_data] : storage_) {
@ -324,6 +328,7 @@ void TriggerStore::AddTrigger(const std::string &name, const std::string &query,
e.what(), TriggerEventTypeToString(event_type), identifier_names_stream.str()); e.what(), TriggerEventTypeToString(event_type), identifier_names_stream.str());
} }
// When the format of the persisted trigger is changed, update the kVersion
nlohmann::json data = nlohmann::json::object(); nlohmann::json data = nlohmann::json::object();
data["statement"] = query; data["statement"] = query;
data["user_parameters"] = serialization::SerializePropertyValueMap(user_parameters); data["user_parameters"] = serialization::SerializePropertyValueMap(user_parameters);

View File

@ -60,8 +60,10 @@ struct Trigger {
enum class TriggerPhase : uint8_t { BEFORE_COMMIT, AFTER_COMMIT }; enum class TriggerPhase : uint8_t { BEFORE_COMMIT, AFTER_COMMIT };
struct TriggerStore { struct TriggerStore {
explicit TriggerStore(std::filesystem::path directory, utils::SkipList<QueryCacheEntry> *query_cache, explicit TriggerStore(std::filesystem::path directory);
DbAccessor *db_accessor, utils::SpinLock *antlr_lock);
void RestoreTriggers(utils::SkipList<QueryCacheEntry> *query_cache, DbAccessor *db_accessor,
utils::SpinLock *antlr_lock);
void AddTrigger(const std::string &name, const std::string &query, void AddTrigger(const std::string &name, const std::string &query,
const std::map<std::string, storage::PropertyValue> &user_parameters, TriggerEventType event_type, const std::map<std::string, storage::PropertyValue> &user_parameters, TriggerEventType event_type,

View File

@ -830,10 +830,15 @@ class TriggerStoreTest : public ::testing::Test {
std::optional<storage::Storage::Accessor> storage_accessor; std::optional<storage::Storage::Accessor> storage_accessor;
}; };
TEST_F(TriggerStoreTest, Load) { TEST_F(TriggerStoreTest, Restore) {
std::optional<query::TriggerStore> store; std::optional<query::TriggerStore> store;
store.emplace(testing_directory, &ast_cache, &*dba, &antlr_lock); const auto reset_store = [&] {
store.emplace(testing_directory);
store->RestoreTriggers(&ast_cache, &*dba, &antlr_lock);
};
reset_store();
const auto check_empty = [&] { const auto check_empty = [&] {
ASSERT_EQ(store->GetTriggerInfo().size(), 0); ASSERT_EQ(store->GetTriggerInfo().size(), 0);
@ -879,7 +884,7 @@ TEST_F(TriggerStoreTest, Load) {
check_triggers(); check_triggers();
// recreate trigger store, this should reload everything from the disk // recreate trigger store, this should reload everything from the disk
store.emplace(testing_directory, &ast_cache, &*dba, &antlr_lock); reset_store();
check_triggers(); check_triggers();
ASSERT_NO_THROW(store->DropTrigger(trigger_name_after)); ASSERT_NO_THROW(store->DropTrigger(trigger_name_after));
@ -887,13 +892,13 @@ TEST_F(TriggerStoreTest, Load) {
check_empty(); check_empty();
store.emplace(testing_directory, &ast_cache, &*dba, &antlr_lock); reset_store();
check_empty(); check_empty();
} }
TEST_F(TriggerStoreTest, AddTrigger) { TEST_F(TriggerStoreTest, AddTrigger) {
query::TriggerStore store{testing_directory, &ast_cache, &*dba, &antlr_lock}; query::TriggerStore store{testing_directory};
// Invalid query in statements // Invalid query in statements
ASSERT_THROW(store.AddTrigger("trigger", "RETUR 1", {}, query::TriggerEventType::VERTEX_CREATE, ASSERT_THROW(store.AddTrigger("trigger", "RETUR 1", {}, query::TriggerEventType::VERTEX_CREATE,
@ -926,7 +931,7 @@ TEST_F(TriggerStoreTest, AddTrigger) {
} }
TEST_F(TriggerStoreTest, DropTrigger) { TEST_F(TriggerStoreTest, DropTrigger) {
query::TriggerStore store{testing_directory, &ast_cache, &*dba, &antlr_lock}; query::TriggerStore store{testing_directory};
ASSERT_THROW(store.DropTrigger("Unknown"), utils::BasicException); ASSERT_THROW(store.DropTrigger("Unknown"), utils::BasicException);
@ -940,7 +945,7 @@ TEST_F(TriggerStoreTest, DropTrigger) {
} }
TEST_F(TriggerStoreTest, TriggerInfo) { TEST_F(TriggerStoreTest, TriggerInfo) {
query::TriggerStore store{testing_directory, &ast_cache, &*dba, &antlr_lock}; query::TriggerStore store{testing_directory};
std::vector<query::TriggerStore::TriggerInfo> expected_info; std::vector<query::TriggerStore::TriggerInfo> expected_info;
store.AddTrigger("trigger", "RETURN 1", {}, query::TriggerEventType::VERTEX_CREATE, store.AddTrigger("trigger", "RETURN 1", {}, query::TriggerEventType::VERTEX_CREATE,
@ -985,7 +990,7 @@ TEST_F(TriggerStoreTest, TriggerInfo) {
} }
TEST_F(TriggerStoreTest, AnyTriggerAllKeywords) { TEST_F(TriggerStoreTest, AnyTriggerAllKeywords) {
query::TriggerStore store{testing_directory, &ast_cache, &*dba, &antlr_lock}; query::TriggerStore store{testing_directory};
using namespace std::literals; using namespace std::literals;