Skip to content

Commit 189d3bf

Browse files
Storage: Adapt to ingesting snapshot with irregular region range (#10151) (release-8.5) (#10157)
close #10147 Storage: Adapt to ingesting snapshot with irregular region range If the key format is "t${tableID}_r${handleID1}{AnySuffix}", and `AnySuffix` is not empty, `"t${tableID}_r${handleID1}{AnySuffix}" > "t${tableID}_r${handleID1}"` according to the the comparison semantics of "Key" on TiDB/TiKV. For example, If a table is non-clustered, that is with Int64 as the HandleID type. And the range of a Region is `["xxx_r{handleId1}{AnySuffix}", "xxx_r{handleId2}{AnySuffix}")`, where handleId1 = 100, handleId2 = 200. If `AnySuffix` is empty, then Region contains the left-closed-right-open range of `[handleID1, handleID2)` If `AnySuffix` is not empty, then Region contains the range `(handleID1, handleID2]`, which is actually the left-closed-right-open range of `[handleID1+1, handleID2+1)`. Signed-off-by: ti-chi-bot <[email protected]> Signed-off-by: JaySon-Huang <[email protected]> Co-authored-by: JaySon <[email protected]> Co-authored-by: JaySon-Huang <[email protected]>
1 parent 33821da commit 189d3bf

18 files changed

+372
-180
lines changed

dbms/src/Storages/DeltaMerge/DeltaMergeStore_Ingest.cpp

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include <Storages/KVStore/MultiRaft/Disagg/FastAddPeerContext.h>
3030
#include <Storages/KVStore/TMTContext.h>
3131
#include <Storages/PathPool.h>
32+
#include <common/logger_useful.h>
3233

3334
#include <magic_enum.hpp>
3435

@@ -131,7 +132,7 @@ void DeltaMergeStore::cleanPreIngestFiles(
131132
{
132133
// For disagg mode
133134
// - if the job has been finished, it means the local files is likely all uploaded to S3
134-
// - if the job is intrrupted, it means the `SSTFilesToDTFilesOutputStream::cancel` is called
135+
// - if the job is interrupted, it means the `SSTFilesToDTFilesOutputStream::cancel` is called
135136
// and local files are also removed.
136137
// So we ignore the files on disk.
137138
removePreIngestFile(f.id, false);
@@ -269,9 +270,9 @@ Segments DeltaMergeStore::ingestDTFilesUsingSplit(
269270
/*throw_if_notfound*/ true);
270271

271272
const auto delete_range = remaining_delete_range.shrink(segment->getRowKeyRange());
273+
// as remaining_delete_range is not none, we expect the shrunk range to be not none.
272274
RUNTIME_CHECK(
273-
!delete_range
274-
.none(), // as remaining_delete_range is not none, we expect the shrinked range to be not none.
275+
!delete_range.none(),
275276
delete_range.toDebugString(),
276277
segment->simpleInfo(),
277278
remaining_delete_range.toDebugString());
@@ -616,13 +617,13 @@ UInt64 DeltaMergeStore::ingestFiles(
616617
}
617618

618619
// Check whether all external files are contained by the range.
619-
if (dm_context->global_context.getSettingsRef().dt_enable_ingest_check)
620+
for (const auto & ext_file : external_files)
620621
{
621-
for (const auto & ext_file : external_files)
622+
if (dm_context->global_context.getSettingsRef().dt_enable_ingest_check)
622623
{
623624
RUNTIME_CHECK_MSG(
624625
range.getStart() <= ext_file.range.getStart() && range.getEnd() >= ext_file.range.getEnd(),
625-
"Detected illegal region boundary: range={} file_range={} keyspace={} table_id={}. "
626+
"Detected illegal region boundary: keyspace={} table_id={} range={} file_range={}. "
626627
"TiFlash will exit to prevent data inconsistency. "
627628
"If you accept data inconsistency and want to continue the service, "
628629
"set profiles.default.dt_enable_ingest_check=false .",
@@ -631,6 +632,21 @@ UInt64 DeltaMergeStore::ingestFiles(
631632
range.toDebugString(),
632633
ext_file.range.toDebugString());
633634
}
635+
else
636+
{
637+
// If the check is disabled, we just log a warning for better diagnosing.
638+
if (unlikely(
639+
!(range.getStart() <= ext_file.range.getStart() && range.getEnd() >= ext_file.range.getEnd())))
640+
{
641+
LOG_WARNING(
642+
log,
643+
"Detected illegal region boundary: keyspace={} table_id={} range={} file_range={}",
644+
keyspace_id,
645+
physical_table_id,
646+
range.toDebugString(),
647+
ext_file.range.toDebugString());
648+
}
649+
}
634650
}
635651
}
636652

@@ -862,9 +878,9 @@ std::vector<SegmentPtr> DeltaMergeStore::ingestSegmentsUsingSplit(
862878
/*throw_if_notfound*/ true);
863879

864880
const auto delete_range = remaining_delete_range.shrink(segment->getRowKeyRange());
881+
// as remaining_delete_range is not none, we expect the shrunk range to be not none.
865882
RUNTIME_CHECK(
866-
!delete_range
867-
.none(), // as remaining_delete_range is not none, we expect the shrinked range to be not none.
883+
!delete_range.none(),
868884
delete_range.toDebugString(),
869885
segment->simpleInfo(),
870886
remaining_delete_range.toDebugString());

dbms/src/Storages/DeltaMerge/RowKeyRange.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,59 @@ const RowKeyValue RowKeyValue::COMMON_HANDLE_MAX_KEY
4646
= RowKeyValue(true, std::make_shared<String>(1, TiDB::CodecFlag::CodecFlagMax), 0);
4747
const RowKeyValue RowKeyValue::EMPTY_STRING_KEY = RowKeyValue(true, std::make_shared<String>(""), 0);
4848

49+
std::pair<RowKeyValue, std::string_view> RowKeyValue::fromHandleWithSuffix(
50+
bool is_common_handle_,
51+
const HandleValuePtr value_)
52+
{
53+
if (is_common_handle_)
54+
{
55+
RowKeyValue rowkey_value(is_common_handle_, value_, 0);
56+
return {rowkey_value, std::string_view{nullptr, 0}};
57+
}
58+
59+
assert(!is_common_handle_);
60+
// According to the tidb encoding rule, the int handle must be larger or equal to 8 bytes.
61+
RUNTIME_CHECK_MSG(
62+
value_->size() >= sizeof(Int64),
63+
"Meet illegal int handle size less than expected, value_size={} value={}",
64+
value_->size(),
65+
Redact::keyToHexString(value_->data(), value_->size()));
66+
67+
size_t cursor = 0;
68+
Int64 int_value = DB::DecodeInt64(cursor, *value_);
69+
if (unlikely(value_->size() > sizeof(UInt64)))
70+
{
71+
// For int type handle, the standard key encoding format should be t{table_id}_r{handle_value}.
72+
// But TiKV may generate region range keys which are not strictly following the standard format.
73+
// More concretely, the key may be t{table_id}_r{handle_value}{any_suffix}.
74+
// We need to adapt the key to the standard format.
75+
// For example, the key may be t100_r1000 + '0x00', '0x01' or any other suffix, we need to adapt it to t100_r1001.
76+
// This is ok, because
77+
// 1) if the key is the start range, then [t100_r1000 + 0x00, xxx) has the same semantics with [t100_r1001, xxx)
78+
// 2) if the key is the end range, then [xxx, t100_r1000 + 0x00) also has the same semantics with [xxx, t100_r1001)
79+
//
80+
// Note if the `int_value` is Int64::max_value,
81+
// it is a value generated by tiflash itself to means +inf()(which is RowKeyValue::INT_HANDLE_MAX_KEY).
82+
// So we can just ignore it.
83+
if (int_value < std::numeric_limits<Int64>::max())
84+
{
85+
int_value = int_value + 1;
86+
WriteBufferFromOwnString ss;
87+
DB::EncodeInt64(int_value, ss);
88+
HandleValuePtr fixed_value = std::make_shared<String>(ss.releaseStr());
89+
return {
90+
RowKeyValue(is_common_handle_, fixed_value, int_value),
91+
std::string_view{value_->data() + sizeof(int_value), value_->size() - sizeof(int_value)},
92+
};
93+
}
94+
else
95+
{
96+
// keep the int_value unchanged if it is RowKeyValue::INT_HANDLE_MAX_KEY
97+
}
98+
}
99+
return {RowKeyValue(is_common_handle_, value_, int_value), std::string_view{nullptr, 0}};
100+
}
101+
49102
RowKeyValue RowKeyValueRef::toRowKeyValue() const
50103
{
51104
if (data == nullptr)

dbms/src/Storages/DeltaMerge/RowKeyRange.h

Lines changed: 42 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,14 @@
2222
#include <Storages/DeltaMerge/DeltaMergeHelpers.h>
2323
#include <Storages/DeltaMerge/dtpb/column_file.pb.h>
2424
#include <Storages/KVStore/Decode/DecodedTiKVKeyValue.h>
25-
#include <Storages/KVStore/MultiRaft/RegionRangeKeys.h>
2625
#include <Storages/KVStore/TiKVHelpers/TiKVRecordFormat.h>
2726
#include <Storages/KVStore/Types.h>
2827
#include <TiDB/Decode/DatumCodec.h>
2928

29+
namespace DB
30+
{
31+
class RegionRangeKeys;
32+
}
3033
namespace DB::DM
3134
{
3235
using HandleValuePtr = std::shared_ptr<String>;
@@ -86,62 +89,9 @@ struct RowKeyValue
8689
, int_value(int_value_)
8790
{}
8891

89-
RowKeyValue(bool is_common_handle_, HandleValuePtr value_)
90-
: is_common_handle(is_common_handle_)
91-
, value(value_)
92-
{
93-
if (is_common_handle)
94-
int_value = 0;
95-
else
96-
{
97-
size_t cursor = 0;
98-
int_value = DB::DecodeInt64(cursor, *value);
99-
if (unlikely(value->size() != sizeof(Int64)))
100-
{
101-
// For int type handle, the standard key enconding format should be t{table_id}_r{handle_value}.
102-
// But TiKV may generate region range keys which are not strictly following the standard format.
103-
// More concretely, the key may be t{table_id}_r{handle_value} + some other bytes.
104-
// We need to adapt the key to the standard format.
105-
// For example, the key may be t100_r1000 + 0x00, we need to adapt it to t100_r1001.
106-
// This is ok, because
107-
// 1) if the key is the start range, then [t100_r1000 + 0x00, xxx) has the same semantics with [t100_r1001, xxx)
108-
// 2) if the key is the end range, then [xxx, t100_r1000 + 0x00) also has the same semantics with [xxx, t100_r1001)
109-
//
110-
// Note if the `int_value` is Int64::max_value,
111-
// it is a value generated by tiflash itself to means +inf()(which is RowKeyValue::INT_HANDLE_MAX_KEY).
112-
// So we can just ignore it.
113-
if (value->size() != sizeof(UInt64) + 1 || value->back() != 0x00)
114-
{
115-
LOG_WARNING(
116-
Logger::get(),
117-
"Meet rowkey {} with unexpected encoding format",
118-
Redact::keyToDebugString(value->data(), value->size()));
119-
}
120-
else
121-
{
122-
if (int_value < std::numeric_limits<Int64>::max())
123-
{
124-
LOG_WARNING(
125-
Logger::get(),
126-
"Meet rowkey {} which has an extra zero suffix",
127-
Redact::keyToDebugString(value->data(), value->size()));
128-
int_value = int_value + 1;
129-
WriteBufferFromOwnString ss;
130-
DB::EncodeInt64(int_value, ss);
131-
value = std::make_shared<String>(ss.releaseStr());
132-
}
133-
else
134-
{
135-
// ignore RowKeyValue::INT_HANDLE_MAX_KEY
136-
}
137-
}
138-
}
139-
}
140-
}
141-
14292
explicit RowKeyValue(const RowKeyValueRef & rowkey_value)
93+
: is_common_handle(rowkey_value.is_common_handle)
14394
{
144-
is_common_handle = rowkey_value.is_common_handle;
14595
if (is_common_handle)
14696
value = std::make_shared<String>(rowkey_value.data, rowkey_value.size);
14797
else
@@ -153,12 +103,32 @@ struct RowKeyValue
153103
int_value = rowkey_value.int_value;
154104
}
155105

156-
static RowKeyValue fromHandle(Handle value)
106+
#ifndef NDEBUG
107+
// Generate from int handle
108+
static RowKeyValue fromIntHandle(Handle value)
157109
{
158110
WriteBufferFromOwnString ss;
159111
DB::EncodeInt64(value, ss);
160112
return RowKeyValue(false, std::make_shared<String>(ss.releaseStr()), value);
161113
}
114+
#endif
115+
116+
static RowKeyValue fromHandle(bool is_common_handle_, const HandleValuePtr value_)
117+
{
118+
return fromHandleWithSuffix(is_common_handle_, value_).first;
119+
}
120+
121+
/**
122+
* Parse the handle from `value_`.
123+
* If the handle is a common handle, the return RowKeyValue is the same with `value_`.
124+
* If the handle is an int handle, the `int_value` will be the int value.
125+
* Specially, for int handle, if there is any suffix rather than the regular format,
126+
* a.k.a "t${tableID}_r{handleID}{AnySuffix}" `AnySuffix` is not empty, then
127+
* the `int_value` will be the next int value, and the `AnySuffix` will be returned.
128+
*
129+
* Note that the `AnySuffix` rely on the lifetime of `value_`
130+
*/
131+
static std::pair<RowKeyValue, std::string_view> fromHandleWithSuffix(bool is_common_handle_, HandleValuePtr value_);
162132

163133
// Format as a string
164134
String toString() const;
@@ -171,18 +141,6 @@ struct RowKeyValue
171141
return RowKeyValueRef{is_common_handle, value->data(), value->size(), int_value};
172142
}
173143

174-
DecodedTiKVKeyPtr toRegionKey(TableID table_id) const
175-
{
176-
// FIXME: move this to TiKVRecordFormat.h
177-
WriteBufferFromOwnString ss;
178-
ss.write('t');
179-
EncodeInt64(table_id, ss);
180-
ss.write('_');
181-
ss.write('r');
182-
String prefix = ss.releaseStr();
183-
return std::make_shared<DecodedTiKVKey>(prefix + *value);
184-
}
185-
186144
bool operator==(const RowKeyValue & v) const
187145
{
188146
return is_common_handle == v.is_common_handle && (*value) == (*v.value) && int_value == v.int_value;
@@ -249,7 +207,7 @@ struct RowKeyValue
249207
readBoolText(is_common_handle, buf);
250208
readStringBinary(value, buf);
251209
HandleValuePtr start_ptr = std::make_shared<String>(value);
252-
return RowKeyValue(is_common_handle, start_ptr);
210+
return RowKeyValue::fromHandle(is_common_handle, start_ptr);
253211
}
254212

255213
bool is_common_handle = false;
@@ -577,8 +535,8 @@ struct RowKeyRange
577535
end_ptr = RowKeyValue::COMMON_HANDLE_MAX_KEY.value;
578536
}
579537
return RowKeyRange(
580-
RowKeyValue(is_common_handle, start_ptr),
581-
RowKeyValue(is_common_handle, end_ptr),
538+
RowKeyValue::fromHandle(is_common_handle, start_ptr),
539+
RowKeyValue::fromHandle(is_common_handle, end_ptr),
582540
is_common_handle,
583541
rowkey_column_size);
584542
}
@@ -598,8 +556,8 @@ struct RowKeyRange
598556
end_ptr = RowKeyValue::COMMON_HANDLE_MAX_KEY.value;
599557
}
600558
return RowKeyRange(
601-
RowKeyValue(is_common_handle, start_ptr),
602-
RowKeyValue(is_common_handle, end_ptr),
559+
RowKeyValue::fromHandle(is_common_handle, start_ptr),
560+
RowKeyValue::fromHandle(is_common_handle, end_ptr),
603561
is_common_handle,
604562
rowkey_column_size);
605563
}
@@ -758,9 +716,10 @@ struct RowKeyRange
758716
String end = ss.releaseStr();
759717
/// when handle_range.end == HandleRange::MAX, according to previous implementation, it should be +Inf
760718
return RowKeyRange(
761-
RowKeyValue(is_common_handle, std::make_shared<String>(start)),
762-
handle_range.end == HandleRange::MAX ? RowKeyValue::COMMON_HANDLE_MAX_KEY
763-
: RowKeyValue(is_common_handle, std::make_shared<String>(end)),
719+
RowKeyValue::fromHandle(is_common_handle, std::make_shared<String>(start)),
720+
handle_range.end == HandleRange::MAX
721+
? RowKeyValue::COMMON_HANDLE_MAX_KEY
722+
: RowKeyValue::fromHandle(is_common_handle, std::make_shared<String>(end)),
764723
/*is_common_handle=*/is_common_handle,
765724
1);
766725
}
@@ -794,64 +753,17 @@ struct RowKeyRange
794753

795754
static RowKeyRange fromRegionRange(
796755
const std::shared_ptr<const RegionRangeKeys> & region_range,
797-
const TableID table_id,
756+
TableID table_id,
798757
bool is_common_handle,
799-
size_t rowkey_column_size)
800-
{
801-
return fromRegionRange(
802-
region_range->rawKeys(),
803-
region_range->getMappedTableID(),
804-
table_id,
805-
is_common_handle,
806-
rowkey_column_size);
807-
}
758+
size_t rowkey_column_size,
759+
const String & tracing_msg = "");
808760
static RowKeyRange fromRegionRange(
809761
const std::pair<DecodedTiKVKeyPtr, DecodedTiKVKeyPtr> & raw_keys,
810-
const TableID table_id_in_raw_key,
811-
const TableID table_id,
762+
TableID table_id_in_raw_key,
763+
TableID table_id,
812764
bool is_common_handle,
813-
size_t rowkey_column_size)
814-
{
815-
if (likely(table_id_in_raw_key == table_id))
816-
{
817-
auto & start_key = *raw_keys.first;
818-
auto & end_key = *raw_keys.second;
819-
auto keyspace_id = start_key.getKeyspaceID();
820-
const auto & table_range_min_max = getTableMinMaxData(keyspace_id, table_id, is_common_handle);
821-
RowKeyValue start_value, end_value;
822-
if (start_key <= *table_range_min_max.min)
823-
{
824-
if (is_common_handle)
825-
start_value = RowKeyValue::COMMON_HANDLE_MIN_KEY;
826-
else
827-
start_value = RowKeyValue::INT_HANDLE_MIN_KEY;
828-
}
829-
else
830-
{
831-
start_value = RowKeyValue(
832-
is_common_handle,
833-
std::make_shared<std::string>(RecordKVFormat::getRawTiDBPKView(start_key)));
834-
}
835-
if (end_key >= *table_range_min_max.max)
836-
{
837-
if (is_common_handle)
838-
end_value = RowKeyValue::COMMON_HANDLE_MAX_KEY;
839-
else
840-
end_value = RowKeyValue::INT_HANDLE_MAX_KEY;
841-
}
842-
else
843-
end_value = RowKeyValue(
844-
is_common_handle,
845-
std::make_shared<std::string>(RecordKVFormat::getRawTiDBPKView(end_key)));
846-
return RowKeyRange(start_value, end_value, is_common_handle, rowkey_column_size);
847-
}
848-
else
849-
{
850-
/// if table id is not the same, just return none range
851-
/// maybe should throw exception since it should not happen
852-
return newNone(is_common_handle, rowkey_column_size);
853-
}
854-
}
765+
size_t rowkey_column_size,
766+
const String & tracing_msg);
855767

856768
// Format as a string
857769
String toString() const;

0 commit comments

Comments
 (0)