Files
scylladb/test/boost/frozen_mutation_test.cc
Avi Kivity 69a385fd9d Introduce schema/ module
Schema related files are moved there. This excludes schema files that
also interact with mutations, because the mutation module depends on
the schema. Those files will have to go into a separate module.

Closes #12858
2023-02-15 11:01:50 +02:00

217 lines
8.4 KiB
C++

/*
* Copyright (C) 2015-present ScyllaDB
*/
/*
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#include <boost/test/unit_test.hpp>
#include "test/lib/scylla_test_case.hh"
#include <seastar/testing/thread_test_case.hh>
#include <seastar/util/closeable.hh>
#include "mutation/frozen_mutation.hh"
#include "schema/schema_builder.hh"
#include "mutation/mutation_partition_view.hh"
#include "test/lib/mutation_assertions.hh"
#include "test/lib/mutation_source_test.hh"
#include "test/lib/reader_concurrency_semaphore.hh"
#include <seastar/core/thread.hh>
#include <seastar/core/coroutine.hh>
#include "readers/from_mutations_v2.hh"
#include "readers/mutation_fragment_v1_stream.hh"
static schema_builder new_table() {
return { "some_keyspace", "some_table" };
}
static api::timestamp_type new_timestamp() {
static api::timestamp_type t = 0;
return t++;
};
static tombstone new_tombstone() {
return { new_timestamp(), gc_clock::now() };
};
template <typename UnfreezeFunc>
requires std::same_as<std::invoke_result_t<UnfreezeFunc, const frozen_mutation&, schema_ptr>, mutation>
static future<> _test_freeze_unfreeze(UnfreezeFunc&& unfreeze_func) {
return seastar::async([unfreeze_func = std::move(unfreeze_func)] {
for_each_mutation([&unfreeze_func](const mutation &m) {
auto frozen = freeze(m);
BOOST_REQUIRE_EQUAL(frozen.schema_version(), m.schema()->version());
assert_that(unfreeze_func(frozen, m.schema())).is_equal_to(m);
BOOST_REQUIRE(frozen.decorated_key(*m.schema()).equal(*m.schema(), m.decorated_key()));
});
});
}
SEASTAR_TEST_CASE(test_writing_and_reading) {
return _test_freeze_unfreeze([] (const frozen_mutation& fm, schema_ptr s) {
return fm.unfreeze(std::move(s));
});
}
SEASTAR_TEST_CASE(test_writing_and_reading_gently) {
return _test_freeze_unfreeze([] (const frozen_mutation& fm, schema_ptr s) {
return fm.unfreeze_gently(std::move(s)).handle_exception([] (std::exception_ptr ex) {
testlog.error("test_writing_and_reading_gently: {}", ex);
return make_exception_future<mutation>(std::move(ex));
}).get();
});
}
SEASTAR_TEST_CASE(test_application_of_partition_view_has_the_same_effect_as_applying_regular_mutation) {
return seastar::async([] {
mutation_application_stats app_stats;
schema_ptr s = new_table()
.with_column("pk_col", bytes_type, column_kind::partition_key)
.with_column("ck_1", bytes_type, column_kind::clustering_key)
.with_column("reg_1", bytes_type)
.with_column("reg_2", bytes_type)
.with_column("static_1", bytes_type, column_kind::static_column)
.build();
partition_key key = partition_key::from_single_value(*s, bytes("key"));
clustering_key ck = clustering_key::from_deeply_exploded(*s, {data_value(bytes("ck"))});
mutation m1(s, key);
m1.partition().apply(new_tombstone());
m1.set_clustered_cell(ck, "reg_1", data_value(bytes("val1")), new_timestamp());
m1.set_clustered_cell(ck, "reg_2", data_value(bytes("val2")), new_timestamp());
m1.partition().apply_insert(*s, ck, new_timestamp());
m1.set_static_cell("static_1", data_value(bytes("val3")), new_timestamp());
mutation m2(s, key);
m2.set_clustered_cell(ck, "reg_1", data_value(bytes("val4")), new_timestamp());
m2.partition().apply_insert(*s, ck, new_timestamp());
m2.set_static_cell("static_1", data_value(bytes("val5")), new_timestamp());
mutation m_frozen(s, key);
m_frozen.partition().apply(*s, freeze(m1).partition(), *s, app_stats);
m_frozen.partition().apply(*s, freeze(m2).partition(), *s, app_stats);
mutation m_unfrozen(s, key);
m_unfrozen.partition().apply(*s, m1.partition(), *s, app_stats);
m_unfrozen.partition().apply(*s, m2.partition(), *s, app_stats);
mutation m_refrozen(s, key);
m_refrozen.partition().apply(*s, freeze(m1).unfreeze(s).partition(), *s, app_stats);
m_refrozen.partition().apply(*s, freeze(m2).unfreeze(s).partition(), *s, app_stats);
assert_that(m_unfrozen).is_equal_to(m_refrozen);
assert_that(m_unfrozen).is_equal_to(m_frozen);
});
}
SEASTAR_THREAD_TEST_CASE(test_frozen_mutation_fragment) {
tests::reader_concurrency_semaphore_wrapper semaphore;
for_each_mutation([&] (const mutation& m) {
auto& s = *m.schema();
std::vector<mutation_fragment> mfs;
auto rd = mutation_fragment_v1_stream{make_flat_mutation_reader_from_mutations_v2(m.schema(), semaphore.make_permit(), { m })};
auto close_rd = deferred_close(rd);
rd.consume_pausable([&] (mutation_fragment mf) {
mfs.emplace_back(std::move(mf));
return stop_iteration::no;
}).get();
auto permit = semaphore.make_permit();
for (auto&& mf : mfs) {
auto refrozen_mf = freeze(s, mf).unfreeze(s, permit);
if (!mf.equal(s, refrozen_mf)) {
BOOST_FAIL("Expected " << mutation_fragment::printer(s, mf) << " got " << mutation_fragment::printer(s, refrozen_mf));
}
}
});
}
SEASTAR_TEST_CASE(test_deserialization_using_wrong_schema_throws) {
return seastar::async([] {
schema_ptr s1 = new_table()
.with_column("pk_col", bytes_type, column_kind::partition_key)
.with_column("reg_1", bytes_type)
.with_column("reg_2", bytes_type)
.build();
schema_ptr s2 = new_table()
.with_column("pk_col", bytes_type, column_kind::partition_key)
.with_column("reg_0", bytes_type)
.with_column("reg_1", bytes_type)
.with_column("reg_2", bytes_type)
.build();
schema_ptr s3 = new_table()
.with_column("pk_col", bytes_type, column_kind::partition_key)
.with_column("reg_3", bytes_type)
.without_column("reg_0", new_timestamp())
.without_column("reg_1", new_timestamp())
.build();
schema_ptr s4 = new_table()
.with_column("pk_col", bytes_type, column_kind::partition_key)
.with_column("reg_1", int32_type)
.with_column("reg_2", int32_type)
.build();
partition_key key = partition_key::from_single_value(*s1, bytes("key"));
clustering_key ck = clustering_key::make_empty();
mutation m(s1, key);
m.set_clustered_cell(ck, "reg_1", data_value(bytes("val1")), new_timestamp());
m.set_clustered_cell(ck, "reg_2", data_value(bytes("val2")), new_timestamp());
for (auto s : {s2, s3, s4}) {
BOOST_REQUIRE_THROW(freeze(m).unfreeze(s), schema_mismatch_error);
}
});
}
SEASTAR_TEST_CASE(frozen_mutation_is_consumed_in_order) {
random_mutation_generator gen{random_mutation_generator::generate_counters::no};
mutation m = gen();
auto& s = m.schema();
frozen_mutation fm{m};
auto validate_consume = [] (schema_ptr s, const frozen_mutation& fm, const mutation& m) {
testlog.info("Validating frozen_mutation::consume");
auto c = validating_consumer(*s);
fm.consume(s, c);
testlog.info("Validating frozen_mutation::consume: rebuilding mutation");
mutation_rebuilder_v2 rebuilder(s);
auto rebuilt_mut = fm.consume(s, rebuilder).result;
assert_that(rebuilt_mut).has_mutation();
assert_that(std::move(*rebuilt_mut)).is_equal_to(m);
};
auto validate_consume_gently = [] (schema_ptr s, const frozen_mutation& fm, const mutation& m) -> future<> {
testlog.info("Validating frozen_mutation::consume_gently");
auto c = validating_consumer(*s);
auto adaptor = frozen_mutation_consumer_adaptor(s, c);
co_await fm.consume_gently(s, adaptor);
testlog.info("Validating frozen_mutation::consume_gently: rebuilding mutation");
mutation_rebuilder_v2 rebuilder(s);
auto rebuilt_mut = fm.consume(s, rebuilder).result;
assert_that(rebuilt_mut).has_mutation();
assert_that(std::move(*rebuilt_mut)).is_equal_to(m);
};
validate_consume(s, fm, m);
co_await validate_consume_gently(s, fm, m);
// Add another random mutation and re-test
testlog.info("Adding another random partition to mutation");
m += gen();
fm = freeze(m);
validate_consume(s, fm, m);
co_await validate_consume_gently(s, fm, m);
}