Add tests for flags
This commit is contained in:
parent
675c166254
commit
4dc6400681
@ -57,6 +57,10 @@ DEFINE_bool(cartesian_product_enabled, true, "Enable cartesian product expansion
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_int64(maximum_deltas_per_transaction, -1, "Limit of deltas per transaction, default -1 (no limit)");
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_int64(maximum_delete_deltas_per_transaction, -1,
|
||||
"Limit of delete deltas per transaction, default -1 (no limit)");
|
||||
|
||||
namespace {
|
||||
// Bolt server name
|
||||
constexpr auto kServerNameSettingKey = "server.name";
|
||||
@ -79,6 +83,9 @@ constexpr auto kCartesianProductEnabledGFlagsKey = "cartesian-product-enabled";
|
||||
constexpr auto kMaximumDeltasPerTransactionSettingKey = "maximum-deltas-per-transaction";
|
||||
constexpr auto kMaximumDeltasPerTransactionGFlagsKey = "maximum-deltas-per-transaction";
|
||||
|
||||
constexpr auto kMaximumDeleteDeltasPerTransactionSettingKey = "maximum-delete-deltas-per-transaction";
|
||||
constexpr auto kMaximumDeleteDeltasPerTransactionGFlagsKey = "maximum-delete-deltas-per-transaction";
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
std::atomic<double> execution_timeout_sec_; // Local cache-like thing
|
||||
|
||||
@ -88,6 +95,9 @@ std::atomic<bool> cartesian_product_enabled_{true}; // Local cache-like thing
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
std::atomic<int64_t> maximum_deltas_per_transaction_; // Local cache-like thing
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
std::atomic<int64_t> maximum_delete_deltas_per_transaction_; // Local cache-like thing
|
||||
|
||||
auto ToLLEnum(std::string_view val) {
|
||||
const auto ll_enum = memgraph::flags::LogLevelToEnum(val);
|
||||
if (!ll_enum) {
|
||||
@ -203,6 +213,14 @@ void Initialize() {
|
||||
[&](const std::string &val) {
|
||||
maximum_deltas_per_transaction_ = std::stoll(val); // Cache for faster reads
|
||||
});
|
||||
|
||||
/*
|
||||
* Register maximum delete deltas per transaction
|
||||
*/
|
||||
register_flag(kMaximumDeleteDeltasPerTransactionGFlagsKey, kMaximumDeleteDeltasPerTransactionSettingKey, !kRestore,
|
||||
[&](const std::string &val) {
|
||||
maximum_delete_deltas_per_transaction_ = std::stoll(val); // Cache for faster reads
|
||||
});
|
||||
}
|
||||
|
||||
std::string GetServerName() {
|
||||
@ -218,4 +236,6 @@ bool GetCartesianProductEnabled() { return cartesian_product_enabled_; }
|
||||
|
||||
int64_t GetMaximumDeltasPerTransaction() { return maximum_deltas_per_transaction_; }
|
||||
|
||||
int64_t GetMaximumDeleteDeltasPerTransaction() { return maximum_delete_deltas_per_transaction_; }
|
||||
|
||||
} // namespace memgraph::flags::run_time
|
||||
|
@ -49,4 +49,11 @@ bool GetCartesianProductEnabled();
|
||||
*/
|
||||
int64_t GetMaximumDeltasPerTransaction();
|
||||
|
||||
/**
|
||||
* @brief Get the maximum amount of delete deltas per transaction
|
||||
*
|
||||
* @return int64_t
|
||||
*/
|
||||
int64_t GetMaximumDeleteDeltasPerTransaction();
|
||||
|
||||
} // namespace memgraph::flags::run_time
|
||||
|
@ -1308,8 +1308,15 @@ Transaction InMemoryStorage::CreateTransaction(
|
||||
}
|
||||
|
||||
auto maximum_deltas_per_transaction = flags::run_time::GetMaximumDeltasPerTransaction();
|
||||
auto maximum_delete_deltas_per_transaction = flags::run_time::GetMaximumDeleteDeltasPerTransaction();
|
||||
|
||||
return {transaction_id, start_timestamp, isolation_level, storage_mode, false, maximum_deltas_per_transaction};
|
||||
return {transaction_id,
|
||||
start_timestamp,
|
||||
isolation_level,
|
||||
storage_mode,
|
||||
false,
|
||||
maximum_deltas_per_transaction,
|
||||
maximum_delete_deltas_per_transaction};
|
||||
}
|
||||
|
||||
void InMemoryStorage::SetStorageMode(StorageMode new_storage_mode) {
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "storage/v2/property_value.hpp"
|
||||
#include "storage/v2/transaction.hpp"
|
||||
@ -24,6 +25,9 @@
|
||||
|
||||
namespace memgraph::storage {
|
||||
|
||||
const std::unordered_set<Delta::Action> delete_delta_actions{Delta::Action::ADD_IN_EDGE, Delta::Action::ADD_OUT_EDGE,
|
||||
Delta::Action::RECREATE_OBJECT};
|
||||
|
||||
/// This function iterates through the undo buffers from an object (starting
|
||||
/// from the supplied delta) and determines what deltas should be applied to get
|
||||
/// the currently visible version of the object. When the function finds a delta
|
||||
@ -105,11 +109,18 @@ inline bool PrepareForWrite(Transaction *transaction, TObj *object) {
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void IncrementDeltasChanged(Transaction *transaction) {
|
||||
transaction->deltas_changed++;
|
||||
if (transaction->max_deltas > -1 && transaction->deltas_changed > transaction->max_deltas) {
|
||||
inline void IncrementDeltasChanged(Transaction *transaction, Delta *delta) {
|
||||
if (transaction->max_deltas > -1) {
|
||||
transaction->deltas_changed++;
|
||||
if (transaction->deltas_changed > transaction->max_deltas) {
|
||||
throw utils::BasicException(
|
||||
"You have reached the maximum number of deltas for a transaction, transaction will be rollbacked!");
|
||||
}
|
||||
}
|
||||
|
||||
if (transaction->max_delete_deltas > -1 && delete_delta_actions.contains(delta->action)) {
|
||||
throw utils::BasicException(
|
||||
"You have reached the maximum number of deltas for a transaction, transaction will be rollbacked!");
|
||||
"You have reached the maximum number of delete deltas for a transaction, transaction will be rollbacked!");
|
||||
}
|
||||
}
|
||||
|
||||
@ -124,21 +135,12 @@ inline Delta *CreateDeleteObjectDelta(Transaction *transaction) {
|
||||
}
|
||||
transaction->EnsureCommitTimestampExists();
|
||||
|
||||
IncrementDeltasChanged(transaction);
|
||||
auto *delta = &transaction->deltas.use().emplace_back(Delta::DeleteObjectTag(), transaction->commit_timestamp.get(),
|
||||
transaction->command_id);
|
||||
|
||||
return &transaction->deltas.use().emplace_back(Delta::DeleteObjectTag(), transaction->commit_timestamp.get(),
|
||||
transaction->command_id);
|
||||
}
|
||||
IncrementDeltasChanged(transaction, delta);
|
||||
|
||||
inline Delta *CreateDeleteObjectDelta(Transaction *transaction, std::list<Delta> *deltas) {
|
||||
if (transaction->storage_mode == StorageMode::IN_MEMORY_ANALYTICAL) {
|
||||
return nullptr;
|
||||
}
|
||||
transaction->EnsureCommitTimestampExists();
|
||||
|
||||
IncrementDeltasChanged(transaction);
|
||||
|
||||
return &deltas->emplace_back(Delta::DeleteObjectTag(), transaction->commit_timestamp.get(), transaction->command_id);
|
||||
return delta;
|
||||
}
|
||||
|
||||
/// TODO: what if in-memory analytical
|
||||
@ -181,11 +183,11 @@ inline void CreateAndLinkDelta(Transaction *transaction, TObj *object, Args &&..
|
||||
}
|
||||
transaction->EnsureCommitTimestampExists();
|
||||
|
||||
IncrementDeltasChanged(transaction);
|
||||
|
||||
auto delta = &transaction->deltas.use().emplace_back(std::forward<Args>(args)..., transaction->commit_timestamp.get(),
|
||||
transaction->command_id);
|
||||
|
||||
IncrementDeltasChanged(transaction, delta);
|
||||
|
||||
// The operations are written in such order so that both `next` and `prev`
|
||||
// chains are valid at all times. The chains must be valid at all times
|
||||
// because garbage collection (which traverses the chains) is done
|
||||
|
@ -43,13 +43,15 @@ using PmrListDelta = utils::pmr::list<Delta>;
|
||||
|
||||
struct Transaction {
|
||||
Transaction(uint64_t transaction_id, uint64_t start_timestamp, IsolationLevel isolation_level,
|
||||
StorageMode storage_mode, bool edge_import_mode_active, int64_t max_deltas = -1)
|
||||
StorageMode storage_mode, bool edge_import_mode_active, int64_t max_deltas = -1,
|
||||
int64_t max_delete_deltas = -1)
|
||||
: transaction_id(transaction_id),
|
||||
start_timestamp(start_timestamp),
|
||||
command_id(0),
|
||||
deltas(0),
|
||||
md_deltas(utils::NewDeleteResource()),
|
||||
max_deltas(max_deltas),
|
||||
max_delete_deltas(max_delete_deltas),
|
||||
must_abort(false),
|
||||
isolation_level(isolation_level),
|
||||
storage_mode(storage_mode),
|
||||
@ -94,8 +96,10 @@ struct Transaction {
|
||||
|
||||
Bond<PmrListDelta> deltas;
|
||||
utils::pmr::list<MetadataDelta> md_deltas;
|
||||
int64_t max_deltas{0};
|
||||
int64_t max_deltas{-1};
|
||||
int64_t max_delete_deltas{-1};
|
||||
uint64_t deltas_changed{0};
|
||||
uint64_t delete_deltas_changed{0};
|
||||
bool must_abort{};
|
||||
IsolationLevel isolation_level{};
|
||||
StorageMode storage_mode{};
|
||||
|
@ -76,6 +76,7 @@ add_subdirectory(queries)
|
||||
add_subdirectory(query_modules_storage_modes)
|
||||
add_subdirectory(garbage_collection)
|
||||
add_subdirectory(query_planning)
|
||||
add_subdirectory(maximum_deltas_restriction)
|
||||
|
||||
if (MG_EXPERIMENTAL_HIGH_AVAILABILITY)
|
||||
add_subdirectory(high_availability_experimental)
|
||||
|
@ -90,6 +90,8 @@ startup_config_dict = {
|
||||
"TRACE",
|
||||
"Minimum log level. Allowed values: TRACE, DEBUG, INFO, WARNING, ERROR, CRITICAL",
|
||||
),
|
||||
"maximum_deltas_per_transaction": ("-1", "-1", "Limit of deltas per transaction, default -1 (no limit)"),
|
||||
"maximum_delete_deltas_per_transaction": ("-1", "-1", "Limit of deltas per transaction, default -1 (no limit)"),
|
||||
"memory_limit": (
|
||||
"0",
|
||||
"0",
|
||||
|
8
tests/e2e/maximum_deltas_restriction/CMakeLists.txt
Normal file
8
tests/e2e/maximum_deltas_restriction/CMakeLists.txt
Normal file
@ -0,0 +1,8 @@
|
||||
function(copy_maximum_deltas_restriction_e2e_python_files FILE_NAME)
|
||||
copy_e2e_python_files(maximum_deltas_restriction ${FILE_NAME})
|
||||
endfunction()
|
||||
|
||||
copy_maximum_deltas_restriction_e2e_python_files(common.py)
|
||||
copy_maximum_deltas_restriction_e2e_python_files(maximum_deltas_restriction.py)
|
||||
|
||||
copy_e2e_files(maximum_deltas_restriction workloads.yaml)
|
27
tests/e2e/maximum_deltas_restriction/common.py
Normal file
27
tests/e2e/maximum_deltas_restriction/common.py
Normal file
@ -0,0 +1,27 @@
|
||||
# Copyright 2023 Memgraph Ltd.
|
||||
#
|
||||
# Use of this software is governed by the Business Source License
|
||||
# included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
# License, and you may not use this file except in compliance with the Business Source License.
|
||||
#
|
||||
# As of the Change Date specified in that file, in accordance with
|
||||
# the Business Source License, use of this software will be governed
|
||||
# by the Apache License, Version 2.0, included in the file
|
||||
# licenses/APL.txt.
|
||||
|
||||
import pytest
|
||||
from gqlalchemy import Memgraph
|
||||
|
||||
MAXIMUM_DELTAS_RESTRICTION_PLACEHOLDER = "SET DATABASE SETTING 'maximum-deltas-per-transaction' TO '$0';"
|
||||
MAXIMUM_DELETE_DELTAS_RESTRICTION_PLACEHOLDER = "SET DATABASE SETTING 'maximum-delete-deltas-per-transaction' TO '$0';"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def memgraph(**kwargs) -> Memgraph:
|
||||
memgraph = Memgraph()
|
||||
|
||||
yield memgraph
|
||||
|
||||
memgraph.execute(MAXIMUM_DELTAS_RESTRICTION_PLACEHOLDER.replace("$0", "-1"))
|
||||
memgraph.execute(MAXIMUM_DELETE_DELTAS_RESTRICTION_PLACEHOLDER.replace("$0", "-1"))
|
||||
memgraph.drop_database()
|
@ -0,0 +1,74 @@
|
||||
# Copyright 2023 Memgraph Ltd.
|
||||
#
|
||||
# Use of this software is governed by the Business Source License
|
||||
# included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
# License, and you may not use this file except in compliance with the Business Source License.
|
||||
#
|
||||
# As of the Change Date specified in that file, in accordance with
|
||||
# the Business Source License, use of this software will be governed
|
||||
# by the Apache License, Version 2.0, included in the file
|
||||
# licenses/APL.txt.
|
||||
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
from common import (
|
||||
MAXIMUM_DELETE_DELTAS_RESTRICTION_PLACEHOLDER,
|
||||
MAXIMUM_DELTAS_RESTRICTION_PLACEHOLDER,
|
||||
memgraph,
|
||||
)
|
||||
from gqlalchemy import GQLAlchemyError
|
||||
|
||||
CREATE_EMPTY_NODES_PLACEHOLDER = "FOREACH (i in range(1, $0) | CREATE ());"
|
||||
CREATE_FULL_NODES_PLACEHOLDER = "FOREACH (i in range(1, $0) | CREATE (:Node {id: i}));"
|
||||
DELETE_EVERYTHING_QUERY = "MATCH (n) DETACH DELETE n;"
|
||||
|
||||
|
||||
def test_given_no_restrictions_on_the_database_when_executing_commands_then_everything_should_pass(memgraph):
|
||||
memgraph.execute(CREATE_EMPTY_NODES_PLACEHOLDER.replace("$0", "100"))
|
||||
memgraph.execute(CREATE_FULL_NODES_PLACEHOLDER.replace("$0", "100"))
|
||||
memgraph.execute(DELETE_EVERYTHING_QUERY)
|
||||
|
||||
|
||||
def test_given_maximum_restriction_ingestion_fails_when_inserting_more_nodes(memgraph):
|
||||
memgraph.execute(MAXIMUM_DELTAS_RESTRICTION_PLACEHOLDER.replace("$0", "100"))
|
||||
|
||||
memgraph.execute(CREATE_EMPTY_NODES_PLACEHOLDER.replace("$0", "100"))
|
||||
memgraph.execute(DELETE_EVERYTHING_QUERY)
|
||||
|
||||
with pytest.raises(GQLAlchemyError):
|
||||
memgraph.execute(CREATE_EMPTY_NODES_PLACEHOLDER.replace("$0", "101"))
|
||||
|
||||
|
||||
def test_given_maximum_delete_restriction_deletion_fails_when_deleting_more_nodes(memgraph):
|
||||
memgraph.execute(MAXIMUM_DELETE_DELTAS_RESTRICTION_PLACEHOLDER.replace("$0", "100"))
|
||||
|
||||
memgraph.execute(CREATE_EMPTY_NODES_PLACEHOLDER.replace("$0", "2000"))
|
||||
|
||||
with pytest.raises(GQLAlchemyError):
|
||||
memgraph.execute(DELETE_EVERYTHING_QUERY)
|
||||
|
||||
|
||||
def test_given_maximum_delete_restriction_deletion_fails_when_deleting_more_edges(memgraph):
|
||||
memgraph.execute(MAXIMUM_DELETE_DELTAS_RESTRICTION_PLACEHOLDER.replace("$0", "100"))
|
||||
|
||||
memgraph.execute(CREATE_EMPTY_NODES_PLACEHOLDER.replace("$0", "2000"))
|
||||
memgraph.execute("CREATE (s:Supernode)")
|
||||
memgraph.execute("MATCH (s:Supernode) MATCH (n) CREATE (s)-[:HAS]->(n)")
|
||||
|
||||
with pytest.raises(GQLAlchemyError):
|
||||
memgraph.execute(DELETE_EVERYTHING_QUERY)
|
||||
|
||||
|
||||
def test_given_maximum_delta_restriction_fails_when_deleting_everything_on_batch_ingested_nodes(memgraph):
|
||||
memgraph.execute(MAXIMUM_DELTAS_RESTRICTION_PLACEHOLDER.replace("$0", "100"))
|
||||
|
||||
memgraph.execute(CREATE_EMPTY_NODES_PLACEHOLDER.replace("$0", "90"))
|
||||
memgraph.execute(CREATE_EMPTY_NODES_PLACEHOLDER.replace("$0", "90"))
|
||||
|
||||
with pytest.raises(GQLAlchemyError):
|
||||
memgraph.execute(DELETE_EVERYTHING_QUERY)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(pytest.main([__file__, "-rA"]))
|
14
tests/e2e/maximum_deltas_restriction/workloads.yaml
Normal file
14
tests/e2e/maximum_deltas_restriction/workloads.yaml
Normal file
@ -0,0 +1,14 @@
|
||||
maximum_deltas_restriction_cluster: &maximum_deltas_restriction_cluster
|
||||
cluster:
|
||||
main:
|
||||
args: ["--bolt-port", "7687", "--log-level=TRACE"]
|
||||
log_file: "maximum_deltas_restriction.log"
|
||||
setup_queries: []
|
||||
validation_queries: []
|
||||
|
||||
|
||||
workloads:
|
||||
- name: "Maximum deltas restriction"
|
||||
binary: "tests/e2e/pytest_runner.sh"
|
||||
args: ["maximum_deltas_restriction/maximum_deltas_restriction.py"]
|
||||
<<: *maximum_deltas_restriction_cluster
|
Loading…
Reference in New Issue
Block a user