diff --git a/tests/sstable_assertions.hh b/tests/sstable_assertions.hh new file mode 100644 index 0000000000..4379f2cad1 --- /dev/null +++ b/tests/sstable_assertions.hh @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2017 ScyllaDB + */ + +/* + * This file is part of Scylla. + * + * Scylla is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Scylla is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Scylla. If not, see . + */ + +#pragma once + +#include "dht/i_partitioner.hh" +#include "schema.hh" +#include "sstables/index_reader.hh" + +class index_reader_assertions { + std::unique_ptr _r; +public: + index_reader_assertions(std::unique_ptr r) + : _r(std::move(r)) + { } + + index_reader_assertions& has_monotonic_positions(const schema& s) { + auto pos_cmp = position_in_partition::composite_less_compare(s); + auto rp_cmp = dht::ring_position_comparator(s); + auto prev = dht::ring_position::min(); + _r->read_partition_data().get(); + while (!_r->eof()) { + auto k = _r->current_partition_entry().get_decorated_key(); + auto rp = dht::ring_position(k.token(), k.key().to_partition_key(s)); + + if (!rp_cmp(prev, rp)) { + BOOST_FAIL(sprint("Partitions have invalid order: %s >= %s", prev, rp)); + } + + prev = rp; + + auto* pi = _r->current_partition_entry().get_promoted_index(s); + if (!pi->entries.empty()) { + auto& prev = pi->entries[0]; + for (size_t i = 1; i < pi->entries.size(); ++i) { + auto& cur = pi->entries[i]; + if (!pos_cmp(prev.end, cur.start)) { + std::cout << "promoted index:\n"; + for (auto& e : pi->entries) { + std::cout << " " << e.start << "-" << e.end << ": +" << e.offset << " len=" << e.width << std::endl; + } + BOOST_FAIL(sprint("Index blocks are not monotonic: %s >= %s", prev.end, cur.start)); + } + cur = prev; + } + } + _r->advance_to_next_partition().get(); + } + return *this; + } +}; + +inline +index_reader_assertions assert_that(std::unique_ptr r) { + return { std::move(r) }; +} diff --git a/tests/sstable_mutation_test.cc b/tests/sstable_mutation_test.cc index 6bdcddd240..c33cd210e3 100644 --- a/tests/sstable_mutation_test.cc +++ b/tests/sstable_mutation_test.cc @@ -35,6 +35,7 @@ #include "tmpdir.hh" #include "memtable-sstable.hh" #include "disk-error-handler.hh" +#include "tests/sstable_assertions.hh" thread_local disk_error_signal_type commit_error; thread_local disk_error_signal_type general_disk_error; @@ -801,3 +802,52 @@ SEASTAR_TEST_CASE(test_non_compound_table_row_is_not_marked_as_static) { BOOST_REQUIRE(bool(mut)); }); } + +SEASTAR_TEST_CASE(test_promoted_index_blocks_are_monotonic) { + return seastar::async([] { + auto dir = make_lw_shared(); + schema_builder builder("ks", "cf"); + builder.with_column("p", utf8_type, column_kind::partition_key); + builder.with_column("c1", int32_type, column_kind::clustering_key); + builder.with_column("c2", int32_type, column_kind::clustering_key); + builder.with_column("v", int32_type); + auto s = builder.build(); + + auto k = partition_key::from_exploded(*s, {to_bytes("key1")}); + auto cell = atomic_cell::make_live(1, int32_type->decompose(88), { }); + mutation m(k, s); + + auto ck = clustering_key::from_exploded(*s, {int32_type->decompose(1), int32_type->decompose(2)}); + m.set_clustered_cell(ck, *s->get_column_definition("v"), cell); + + ck = clustering_key::from_exploded(*s, {int32_type->decompose(1), int32_type->decompose(4)}); + m.set_clustered_cell(ck, *s->get_column_definition("v"), std::move(cell)); + + ck = clustering_key::from_exploded(*s, {int32_type->decompose(1), int32_type->decompose(6)}); + m.set_clustered_cell(ck, *s->get_column_definition("v"), std::move(cell)); + + ck = clustering_key::from_exploded(*s, {int32_type->decompose(3), int32_type->decompose(9)}); + m.set_clustered_cell(ck, *s->get_column_definition("v"), std::move(cell)); + + m.partition().apply_row_tombstone(*s, range_tombstone( + clustering_key_prefix::from_exploded(*s, {int32_type->decompose(1)}), + bound_kind::excl_start, + clustering_key_prefix::from_exploded(*s, {int32_type->decompose(2)}), + bound_kind::incl_end, + {1, gc_clock::now()})); + + auto mt = make_lw_shared(s); + mt->apply(std::move(m)); + + auto sst = make_lw_shared(s, + dir->path, + 1 /* generation */, + sstables::sstable::version_types::ka, + sstables::sstable::format_types::big); + sstable_writer_config cfg; + cfg.promoted_index_block_size = 1; + sst->write_components(mt->make_reader(s), 1, s, cfg).get(); + sst->load().get(); + assert_that(sst->get_index_reader(default_priority_class())).has_monotonic_positions(*s); + }); +}