Skip to content

Commit

Permalink
[#12211] docdb: Temporary disable tablet splitting for index tables w…
Browse files Browse the repository at this point in the history
…ith range partitioning

Summary:
Issue #12190 gives a potential data loss, a temporary disabling for both dynamic and
manual splitting for index tables with range partitioning is required at master server side.
When #12190 and #12191 are done tablet splitting should be enabled for those tables.
Additional tablet splitting disabling is added at tablet server side, other checks and cases
will be covered in #12189.
Additionally, an index versioning support is implemented by adding a new field
`partition_key_version` into schema's `TableProperties` and `TablePropertiesPB` --
the default version should be updated in the context of #12190, after that tablet splitting
becomes enabled automatically for index tables with range partitioning.

Test Plan:
Main:
1. unit test:
ybd --cxx-test pgwrapper_pg_tablet_split-test --gtest_filter "PgTabletSplitTest.ManualSplitIndexTablet"

2. manual test:
2.1) create a cluster
```
./bin/yb-ctl create --master_flags="enable_automatic_tablet_splitting=true,\
                                   tablet_split_low_phase_shard_count_per_node=16,\
                                   tablet_split_low_phase_size_threshold_bytes=13421" \
                                --tserver_flags="yb_num_shards_per_tserver=1"
```
2.2) run `./bin/ysqlsh` and execute
```
create table employees(id int primary key, name text, description text);
create index idx1 on employees(description ASC);
```
2.3) open tablet server web ui and make sure only one tablet exists for index table
2.4) download `collect_insert_data.txt` from #10926 and rename to `collect_insert_data.py`
2.5) execute `./collect_insert_data.py` to generate sql script
2.6) execute `./bin/ysqlsh -f sql_data.sql`
2.7) make sure only one tablet still exists for index table, while the table has several tablets
2.8) flush `idx1` index tablet: `./bin/yb-ts-cli flush_tablet <UUID>`
2.9) try manual split: `./bin/yb-admin split_tablet -master_addresses localhost:7100 <UUID>`
the following error should appear:
```
Error running split_tablet: Not implemented (yb/master/tablet_split_manager.cc:176):
Unable to start split of tablet <UUID>: Tablet splitting is not supported for the index table "idx1"
with table_id "000033e5000030008000000000004005". Please, rebuild the index!
```

3. Additional tests should not give unexpected results:
ybd --cxx-test tablet_tablet-split-test
ybd --cxx-test integration-tests_tablet-split-itest
ybd --cxx-test integration-tests_tablet_server-itest
ybd --cxx-test integration-tests_cql-tablet-split-test
ybd --cxx-test integration-xcluster-tablet-split-itest
ybd --cxx-test common_schema-test --gtest_filter TestSchema.TestSchema
ybd --cxx-test common_schema-test --gtest_filter TestSchema.TestCreateProjection
ybd --cxx-test master_catalog_manager-test --gtest_filter "TestCatalogManager.CheckIfCanDeleteSingleTablet"
ybd --cxx-test pgwrapper_pg_tablet_split-test --gtest_filter "PgTabletSplitTest.SplitDuringLongRunningTransaction"
ybd --cxx-test pgwrapper_pg_mini-test --gtest_filter "PgMiniTest.TabletSplitSecondaryIndexYSQL"

Reviewers: timur, rthallam

Reviewed By: timur, rthallam

Subscribers: zyu, ybase, bogdan

Differential Revision: https://phabricator.dev.yugabyte.com/D16738
  • Loading branch information
arybochkin committed May 15, 2022
1 parent 2b7f5d1 commit 2ef55c8
Show file tree
Hide file tree
Showing 23 changed files with 460 additions and 198 deletions.
4 changes: 4 additions & 0 deletions src/yb/common/common.proto
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,10 @@ message TablePropertiesPB {
optional bool is_ysql_catalog_table = 8 [ default = false ];
optional bool retain_delete_markers = 9 [ default = false ];
optional uint64 backfilling_timestamp = 10;

// Used to distinguish which algorithm should be used for partition key generation,
// value == 0 stands for the default buggy algorithm for range partitioning case, see #12189.
optional uint32 partition_key_version = 11;
}

message SchemaPB {
Expand Down
4 changes: 4 additions & 0 deletions src/yb/common/partition.cc
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,10 @@ void SetColumnIdentifiers(const vector<ColumnId>& column_ids,

} // namespace

bool PartitionSchema::IsHashPartitioning(const PartitionSchemaPB& pb) {
return pb.has_hash_schema();
}

Status PartitionSchema::FromPB(const PartitionSchemaPB& pb,
const Schema& schema,
PartitionSchema* partition_schema) {
Expand Down
106 changes: 52 additions & 54 deletions src/yb/common/partition.h
Original file line number Diff line number Diff line change
Expand Up @@ -190,25 +190,24 @@ class PartitionSchema {
static constexpr int32_t kMaxPartitionKey = std::numeric_limits<uint16_t>::max();

// Deserializes a protobuf message into a partition schema.
static CHECKED_STATUS FromPB(const PartitionSchemaPB& pb,
const Schema& schema,
PartitionSchema* partition_schema) WARN_UNUSED_RESULT;
static Status FromPB(const PartitionSchemaPB& pb,
const Schema& schema,
PartitionSchema* partition_schema);

static bool IsHashPartitioning(const PartitionSchemaPB& pb);

// Serializes a partition schema into a protobuf message.
void ToPB(PartitionSchemaPB* pb) const;

CHECKED_STATUS EncodeRedisKey(const YBPartialRow& row, std::string* buf) const WARN_UNUSED_RESULT;

CHECKED_STATUS EncodeRedisKey(const ConstContiguousRow& row,
std::string* buf) const WARN_UNUSED_RESULT;

CHECKED_STATUS EncodeRedisKey(const Slice& slice, std::string* buf) const WARN_UNUSED_RESULT;
Status EncodeRedisKey(const YBPartialRow& row, std::string* buf) const;
Status EncodeRedisKey(const ConstContiguousRow& row, std::string* buf) const;
Status EncodeRedisKey(const Slice& slice, std::string* buf) const;

CHECKED_STATUS EncodeKey(const google::protobuf::RepeatedPtrField<QLExpressionPB>& hash_values,
std::string* buf) const WARN_UNUSED_RESULT;
Status EncodeKey(const google::protobuf::RepeatedPtrField<QLExpressionPB>& hash_values,
std::string* buf) const;

template <class Collection>
CHECKED_STATUS EncodePgsqlKey(const Collection& hash_values, std::string* buf) const {
Status EncodePgsqlKey(const Collection& hash_values, std::string* buf) const {
if (!hash_schema_) {
return Status::OK();
}
Expand All @@ -227,12 +226,11 @@ class PartitionSchema {

// Appends the row's encoded partition key into the provided buffer.
// On failure, the buffer may have data partially appended.
CHECKED_STATUS EncodeKey(const YBPartialRow& row, std::string* buf) const WARN_UNUSED_RESULT;
Status EncodeKey(const YBPartialRow& row, std::string* buf) const;

// Appends the row's encoded partition key into the provided buffer.
// On failure, the buffer may have data partially appended.
CHECKED_STATUS EncodeKey(const ConstContiguousRow& row, std::string* buf) const
WARN_UNUSED_RESULT;
Status EncodeKey(const ConstContiguousRow& row, std::string* buf) const;

bool IsHashPartitioning() const;

Expand All @@ -249,8 +247,8 @@ class PartitionSchema {
static uint16_t DecodeMultiColumnHashValue(Slice partition_key);

// Does [partition_key_start, partition_key_end] form a valid range.
static CHECKED_STATUS IsValidHashPartitionRange(const std::string& partition_key_start,
const std::string& partition_key_end);
static Status IsValidHashPartitionRange(const std::string& partition_key_start,
const std::string& partition_key_end);

static bool IsValidHashPartitionKeyBound(const std::string& partition_key);

Expand Down Expand Up @@ -282,7 +280,7 @@ class PartitionSchema {
// TODO(neil) Investigate partitions to support both hash and range schema.
// - First, use range schema to split the table.
// - Second, use hash schema to partition each split.
CHECKED_STATUS CreatePartitions(int32_t num_tablets, std::vector<Partition>* partitions) const;
Status CreatePartitions(int32_t num_tablets, std::vector<Partition>* partitions) const;

// Kudu partition creation
// NOTE: The following function from Kudu is to support a C++ API instead of SQL or CQL. They
Expand All @@ -295,19 +293,19 @@ class PartitionSchema {
// The number of resulting partitions is the product of the number of hash
// buckets for each hash bucket component, multiplied by
// (split_rows.size() + 1).
CHECKED_STATUS CreatePartitions(const std::vector<YBPartialRow>& split_rows,
const Schema& schema,
std::vector<Partition>* partitions) const WARN_UNUSED_RESULT;
Status CreatePartitions(const std::vector<YBPartialRow>& split_rows,
const Schema& schema,
std::vector<Partition>* partitions) const;

// Tests if the partition contains the row.
CHECKED_STATUS PartitionContainsRow(const Partition& partition,
const YBPartialRow& row,
bool* contains) const WARN_UNUSED_RESULT;
Status PartitionContainsRow(const Partition& partition,
const YBPartialRow& row,
bool* contains) const;

// Tests if the partition contains the row.
CHECKED_STATUS PartitionContainsRow(const Partition& partition,
const ConstContiguousRow& row,
bool* contains) const WARN_UNUSED_RESULT;
Status PartitionContainsRow(const Partition& partition,
const ConstContiguousRow& row,
bool* contains) const;

// Returns a text description of the partition suitable for debug printing.
std::string PartitionDebugString(const Partition& partition, const Schema& schema) const;
Expand Down Expand Up @@ -354,53 +352,53 @@ class PartitionSchema {
};

// Convertion between PB and partition schema.
static CHECKED_STATUS KuduFromPB(const PartitionSchemaPB& pb,
const Schema& schema,
PartitionSchema* partition_schema);
static Status KuduFromPB(const PartitionSchemaPB& pb,
const Schema& schema,
PartitionSchema* partition_schema);
void KuduToPB(PartitionSchemaPB* pb) const;

// Creates the set of table partitions using multi column hash schema. In this schema, we divide
// the [ hash(0), hash(max_partition_key) ] range equally into the requested number of intervals.
CHECKED_STATUS CreateHashPartitions(int32_t num_tablets,
std::vector<Partition>* partitions,
int32_t max_partition_key = kMaxPartitionKey) const;
Status CreateHashPartitions(int32_t num_tablets,
std::vector<Partition>* partitions,
int32_t max_partition_key = kMaxPartitionKey) const;

// Creates the set of table partitions using primary-key range schema. In this schema, we divide
// the table by given ranges in the partitions vector.
CHECKED_STATUS CreateRangePartitions(std::vector<Partition>* partitions) const;
Status CreateRangePartitions(std::vector<Partition>* partitions) const;

// Encodes the specified columns of a row into lexicographic sort-order preserving format.
static CHECKED_STATUS EncodeColumns(const YBPartialRow& row,
const std::vector<ColumnId>& column_ids,
std::string* buf);
static Status EncodeColumns(const YBPartialRow& row,
const std::vector<ColumnId>& column_ids,
std::string* buf);

// Encodes the specified columns of a row into lexicographic sort-order preserving format.
static CHECKED_STATUS EncodeColumns(const ConstContiguousRow& row,
const std::vector<ColumnId>& column_ids,
std::string* buf);
static Status EncodeColumns(const ConstContiguousRow& row,
const std::vector<ColumnId>& column_ids,
std::string* buf);

// Hashes a compound string of all columns into a 16-bit integer.
static uint16_t HashColumnCompoundValue(const std::string& compound);

// Encodes the specified columns of a row into 2-byte partition key using the multi column
// hashing scheme.
static CHECKED_STATUS EncodeColumns(const YBPartialRow& row, std::string* buf);
static Status EncodeColumns(const YBPartialRow& row, std::string* buf);

// Encodes the specified columns of a row into 2-byte partition key using the multi column
// hashing scheme.
static CHECKED_STATUS EncodeColumns(const ConstContiguousRow& row, std::string* buf);
static Status EncodeColumns(const ConstContiguousRow& row, std::string* buf);

// Assigns the row to a hash bucket according to the hash schema.
template<typename Row>
static CHECKED_STATUS BucketForRow(const Row& row,
const HashBucketSchema& hash_bucket_schema,
int32_t* bucket);
static Status BucketForRow(const Row& row,
const HashBucketSchema& hash_bucket_schema,
int32_t* bucket);

// Private templated helper for PartitionContainsRow.
template<typename Row>
CHECKED_STATUS PartitionContainsRowImpl(const Partition& partition,
const Row& row,
bool* contains) const;
Status PartitionContainsRowImpl(const Partition& partition,
const Row& row,
bool* contains) const;

// Appends the stringified range partition components of a partial row to a
// vector.
Expand All @@ -421,24 +419,24 @@ class PartitionSchema {

// Decodes a range partition key into a partial row, with variable-length
// fields stored in the arena.
CHECKED_STATUS DecodeRangeKey(Slice* encode_key,
YBPartialRow* partial_row,
Arena* arena) const;
Status DecodeRangeKey(Slice* encode_key,
YBPartialRow* partial_row,
Arena* arena) const;

// Decodes the hash bucket component of a partition key into its buckets.
//
// This should only be called with partition keys created from a row, not with
// partition keys from a partition.
CHECKED_STATUS DecodeHashBuckets(Slice* partition_key, std::vector<int32_t>* buckets) const;
Status DecodeHashBuckets(Slice* partition_key, std::vector<int32_t>* buckets) const;

// Clears the state of this partition schema.
void Clear();

// Validates that this partition schema is valid. Returns OK, or an
// appropriate error code for an invalid partition schema.
CHECKED_STATUS Validate(const Schema& schema) const;
Status Validate(const Schema& schema) const;

static CHECKED_STATUS CompleteEncodeKey(const std::string& temp, std::string* buf);
static Status CompleteEncodeKey(const std::string& temp, std::string* buf);

std::vector<HashBucketSchema> hash_bucket_schemas_;
RangeSchema range_schema_;
Expand Down
52 changes: 30 additions & 22 deletions src/yb/common/schema-test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -69,15 +69,17 @@ TEST(TestSchema, TestSchema) {
ASSERT_GT(schema.memory_footprint_excluding_this(),
empty_schema.memory_footprint_excluding_this());

EXPECT_EQ("Schema [\n"
"\tkey[string NOT NULL NOT A PARTITION KEY],\n"
"\tuint32val[uint32 NULLABLE NOT A PARTITION KEY],\n"
"\tint32val[int32 NOT NULL NOT A PARTITION KEY]\n"
"]\nproperties: contain_counters: false is_transactional: false "
"consistency_level: STRONG "
"use_mangled_column_name: false "
"is_ysql_catalog_table: false "
"retain_delete_markers: false",
EXPECT_EQ(Format("Schema [\n"
"\tkey[string NOT NULL NOT A PARTITION KEY],\n"
"\tuint32val[uint32 NULLABLE NOT A PARTITION KEY],\n"
"\tint32val[int32 NOT NULL NOT A PARTITION KEY]\n"
"]\nproperties: contain_counters: false is_transactional: false "
"consistency_level: STRONG "
"use_mangled_column_name: false "
"is_ysql_catalog_table: false "
"retain_delete_markers: false "
"partition_key_version: $0",
kCurrentPartitionKeyVersion),
schema.ToString());
EXPECT_EQ("key[string NOT NULL NOT A PARTITION KEY]", schema.column(0).ToString());
EXPECT_EQ("uint32 NULLABLE NOT A PARTITION KEY", schema.column(1).TypeToString());
Expand Down Expand Up @@ -234,15 +236,17 @@ TEST(TestSchema, TestCreateProjection) {

// By names, without IDs
ASSERT_OK(schema.CreateProjectionByNames({ "col1", "col2", "col4" }, &partial_schema));
EXPECT_EQ("Schema [\n"
"\tcol1[string NOT NULL NOT A PARTITION KEY],\n"
"\tcol2[string NOT NULL NOT A PARTITION KEY],\n"
"\tcol4[string NOT NULL NOT A PARTITION KEY]\n"
"]\nproperties: contain_counters: false is_transactional: false "
"consistency_level: STRONG "
"use_mangled_column_name: false "
"is_ysql_catalog_table: false "
"retain_delete_markers: false",
EXPECT_EQ(Format("Schema [\n"
"\tcol1[string NOT NULL NOT A PARTITION KEY],\n"
"\tcol2[string NOT NULL NOT A PARTITION KEY],\n"
"\tcol4[string NOT NULL NOT A PARTITION KEY]\n"
"]\nproperties: contain_counters: false is_transactional: false "
"consistency_level: STRONG "
"use_mangled_column_name: false "
"is_ysql_catalog_table: false "
"retain_delete_markers: false "
"partition_key_version: $0",
kCurrentPartitionKeyVersion),
partial_schema.ToString());

// By names, with IDS
Expand All @@ -255,10 +259,12 @@ TEST(TestSchema, TestCreateProjection) {
"consistency_level: STRONG "
"use_mangled_column_name: false "
"is_ysql_catalog_table: false "
"retain_delete_markers: false",
"retain_delete_markers: false "
"partition_key_version: $3",
schema_with_ids.column_id(0),
schema_with_ids.column_id(1),
schema_with_ids.column_id(3)),
schema_with_ids.column_id(3),
kCurrentPartitionKeyVersion),
partial_schema.ToString());

// By names, with missing names.
Expand All @@ -279,10 +285,12 @@ TEST(TestSchema, TestCreateProjection) {
"consistency_level: STRONG "
"use_mangled_column_name: false "
"is_ysql_catalog_table: false "
"retain_delete_markers: false",
"retain_delete_markers: false "
"partition_key_version: $3",
schema_with_ids.column_id(0),
schema_with_ids.column_id(1),
schema_with_ids.column_id(3)),
schema_with_ids.column_id(3),
kCurrentPartitionKeyVersion),
partial_schema.ToString());
}

Expand Down
15 changes: 13 additions & 2 deletions src/yb/common/schema.cc
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@ size_t ColumnSchema::memory_footprint_including_this() const {
// TableProperties
// ------------------------------------------------------------------------------------------------

const TableId kNoCopartitionTableId = "";

void TableProperties::ToTablePropertiesPB(TablePropertiesPB *pb) const {
if (HasDefaultTimeToLive()) {
pb->set_default_time_to_live(default_time_to_live_);
Expand All @@ -150,6 +152,7 @@ void TableProperties::ToTablePropertiesPB(TablePropertiesPB *pb) const {
}
pb->set_is_ysql_catalog_table(is_ysql_catalog_table_);
pb->set_retain_delete_markers(retain_delete_markers_);
pb->set_partition_key_version(partition_key_version_);
}

TableProperties TableProperties::FromTablePropertiesPB(const TablePropertiesPB& pb) {
Expand Down Expand Up @@ -181,6 +184,9 @@ TableProperties TableProperties::FromTablePropertiesPB(const TablePropertiesPB&
if (pb.has_retain_delete_markers()) {
table_properties.SetRetainDeleteMarkers(pb.retain_delete_markers());
}
if (pb.has_partition_key_version()) {
table_properties.set_partition_key_version(pb.partition_key_version());
}
return table_properties;
}

Expand Down Expand Up @@ -209,6 +215,9 @@ void TableProperties::AlterFromTablePropertiesPB(const TablePropertiesPB& pb) {
if (pb.has_retain_delete_markers()) {
SetRetainDeleteMarkers(pb.retain_delete_markers());
}
if (pb.has_partition_key_version()) {
set_partition_key_version(pb.partition_key_version());
}
}

void TableProperties::Reset() {
Expand All @@ -221,6 +230,7 @@ void TableProperties::Reset() {
num_tablets_ = 0;
is_ysql_catalog_table_ = false;
retain_delete_markers_ = false;
partition_key_version_ = kCurrentPartitionKeyVersion;
}

string TableProperties::ToString() const {
Expand All @@ -234,9 +244,10 @@ string TableProperties::ToString() const {
result += Format("copartition_table_id: $0 ", copartition_table_id_);
}
return result + Format(
"consistency_level: $0 is_ysql_catalog_table: $1 }",
"consistency_level: $0 is_ysql_catalog_table: $1 partition_key_version: $2 }",
consistency_level_,
is_ysql_catalog_table_);
is_ysql_catalog_table_,
partition_key_version_);
}

// ------------------------------------------------------------------------------------------------
Expand Down
Loading

0 comments on commit 2ef55c8

Please sign in to comment.