Skip to content

Commit 8ecbea0

Browse files
author
Sivarajan Narayanan
committed
[test](cloud) Time travel: unit tests, regression tests, and design doc
1 parent 0c24299 commit 8ecbea0

22 files changed

Lines changed: 1532 additions & 142 deletions

File tree

be/src/cloud/cloud_meta_mgr.cpp

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -842,10 +842,9 @@ Status CloudMetaMgr::sync_tablet_rowsets_unlocked(CloudTablet* tablet,
842842
// the historical subset — skip them for the time travel path.
843843
tablet->tablet_meta()->delete_bitmap().merge(delete_bitmap);
844844
} else {
845-
auto st = sync_tablet_delete_bitmap(tablet, old_max_version, resp.rowset_meta(),
846-
resp.stats(), req.idx(), &delete_bitmap,
847-
options.full_sync, sync_stats, read_version,
848-
false);
845+
auto st = sync_tablet_delete_bitmap(
846+
tablet, old_max_version, resp.rowset_meta(), resp.stats(), req.idx(),
847+
&delete_bitmap, options.full_sync, sync_stats, read_version, false);
849848
if (st.is<ErrorCode::ROWSETS_EXPIRED>() && tried++ < retry_times) {
850849
LOG_INFO("rowset meta is expired, need to retry, " + tablet_info)
851850
.tag("tried", tried)
@@ -857,9 +856,8 @@ Status CloudMetaMgr::sync_tablet_rowsets_unlocked(CloudTablet* tablet,
857856
return st;
858857
}
859858
tablet->tablet_meta()->delete_bitmap().merge(delete_bitmap);
860-
RETURN_IF_ERROR(_log_mow_delete_bitmap(tablet, resp, delete_bitmap,
861-
old_max_version, options.full_sync,
862-
read_version));
859+
RETURN_IF_ERROR(_log_mow_delete_bitmap(tablet, resp, delete_bitmap, old_max_version,
860+
options.full_sync, read_version));
863861
RETURN_IF_ERROR(
864862
_check_delete_bitmap_v2_correctness(tablet, req, resp, old_max_version));
865863
}

be/src/cloud/cloud_tablet.cpp

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -523,10 +523,8 @@ void CloudTablet::delete_rowsets(const std::vector<RowsetSharedPtr>& to_delete,
523523
}
524524

525525
Status CloudTablet::capture_rs_readers_with_tt_rowsets(
526-
const Version& spec_version,
527-
const std::vector<RowsetSharedPtr>& tt_extra_rowsets,
528-
std::vector<RowSetSplits>* rs_splits,
529-
bool skip_missing_version) {
526+
const Version& spec_version, const std::vector<RowsetSharedPtr>& tt_extra_rowsets,
527+
std::vector<RowSetSplits>* rs_splits, bool skip_missing_version) {
530528
// Build a version path from the shared rowset map, then merge in tt_extra_rowsets
531529
// for versions already compacted away. Only a shared lock is held; tablet state is unchanged.
532530
std::shared_lock rlock(_meta_lock);
@@ -538,16 +536,15 @@ Status CloudTablet::capture_rs_readers_with_tt_rowsets(
538536
for (auto& rs : tt_extra_rowsets) {
539537
if (!rs || rs->end_version() > spec_version.second) continue;
540538
bool covered = std::any_of(path.begin(), path.end(), [&rs](const RowsetSharedPtr& a) {
541-
return a->start_version() <= rs->start_version()
542-
&& rs->end_version() <= a->end_version();
539+
return a->start_version() <= rs->start_version() &&
540+
rs->end_version() <= a->end_version();
543541
});
544542
if (!covered) path.push_back(rs);
545543
}
546544

547-
std::sort(path.begin(), path.end(),
548-
[](const RowsetSharedPtr& a, const RowsetSharedPtr& b) {
549-
return a->start_version() < b->start_version();
550-
});
545+
std::sort(path.begin(), path.end(), [](const RowsetSharedPtr& a, const RowsetSharedPtr& b) {
546+
return a->start_version() < b->start_version();
547+
});
551548

552549
int64_t expected = 0;
553550
for (auto& rs : path) {

be/src/cloud/cloud_tablet.h

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -160,11 +160,10 @@ class CloudTablet final : public BaseTablet {
160160

161161
// Build a read source for spec_version, merging tt_extra_rowsets for compacted-away versions.
162162
// Does not modify shared tablet state; caller provides pre-compaction rowsets from FDB.
163-
Status capture_rs_readers_with_tt_rowsets(
164-
const Version& spec_version,
165-
const std::vector<RowsetSharedPtr>& tt_extra_rowsets,
166-
std::vector<RowSetSplits>* rs_splits,
167-
bool skip_missing_version = false);
163+
Status capture_rs_readers_with_tt_rowsets(const Version& spec_version,
164+
const std::vector<RowsetSharedPtr>& tt_extra_rowsets,
165+
std::vector<RowSetSplits>* rs_splits,
166+
bool skip_missing_version = false);
168167

169168
// MUST hold EXCLUSIVE `_meta_lock`.
170169
void delete_rowsets(const std::vector<RowsetSharedPtr>& to_delete,

be/src/exec/operator/olap_scan_operator.cpp

Lines changed: 16 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "cloud/cloud_tablet.h"
3030
#include "cloud/cloud_tablet_hotspot.h"
3131
#include "cloud/config.h"
32+
#include "cloud/pb_convert.h"
3233
#include "exec/operator/scan_operator.h"
3334
#include "exec/runtime_filter/runtime_filter_consumer_helper.h"
3435
#include "exec/scan/olap_scanner.h"
@@ -47,10 +48,9 @@
4748
#include "runtime/runtime_state.h"
4849
#include "service/backend_options.h"
4950
#include "storage/index/ann/ann_topn_runtime.h"
51+
#include "storage/rowset/rowset_factory.h"
5052
#include "storage/storage_engine.h"
5153
#include "storage/tablet/tablet.h"
52-
#include "cloud/pb_convert.h"
53-
#include "storage/rowset/rowset_factory.h"
5454
#include "storage/tablet/tablet_manager.h"
5555
#include "util/to_string.h"
5656

@@ -844,19 +844,17 @@ Status OlapScanLocalState::_sync_cloud_tablets(RuntimeState* state) {
844844
// FE has already resolved timestamp_ms → partition version and stored it in
845845
// TPaloScanRange.version. tt_timestamp_ms is retained here for MoW delete bitmap capping.
846846
const auto& p = _parent->cast<OlapScanOperatorX>();
847-
const int64_t tt_timestamp_ms =
848-
p._olap_scan_node.__isset.time_travel_timestamp_ms
849-
? p._olap_scan_node.time_travel_timestamp_ms
850-
: -1;
847+
const int64_t tt_timestamp_ms = p._olap_scan_node.__isset.time_travel_timestamp_ms
848+
? p._olap_scan_node.time_travel_timestamp_ms
849+
: -1;
851850

852851
for (size_t i = 0; i < _scan_ranges.size(); i++) {
853852
auto* sync_stats = &_sync_statistics[i];
854853
// Version is read from scan range for both normal and time-travel queries.
855854
// For time travel, FE pre-resolved it via batch getVersionsAtTime().
856855
int64_t version = 0;
857856
std::from_chars(_scan_ranges[i]->version.data(),
858-
_scan_ranges[i]->version.data() +
859-
_scan_ranges[i]->version.size(),
857+
_scan_ranges[i]->version.data() + _scan_ranges[i]->version.size(),
860858
version);
861859
auto task_ctx = state->get_task_execution_context();
862860
auto task_create_time = std::chrono::steady_clock::now();
@@ -881,8 +879,8 @@ Status OlapScanLocalState::_sync_cloud_tablets(RuntimeState* state) {
881879
}
882880
});
883881
// Fetch tablet. Version is already resolved by FE — use it directly.
884-
auto tablet = DORIS_TRY(ExecEnv::get_tablet(
885-
_scan_ranges[i]->tablet_id, sync_stats));
882+
auto tablet =
883+
DORIS_TRY(ExecEnv::get_tablet(_scan_ranges[i]->tablet_id, sync_stats));
886884
_tablets[i] = {std::move(tablet), version, {}};
887885
SyncOptions options;
888886
options.query_version = version;
@@ -911,8 +909,7 @@ Status OlapScanLocalState::_sync_cloud_tablets(RuntimeState* state) {
911909
rs_meta->init_from_pb(rs_meta_pb);
912910
RowsetSharedPtr rs;
913911
auto schema = _tablets[i].tablet->tablet_schema();
914-
if (RowsetFactory::create_rowset(schema, "", rs_meta, &rs)
915-
.ok()) {
912+
if (RowsetFactory::create_rowset(schema, "", rs_meta, &rs).ok()) {
916913
_tablets[i].tt_extra_rowsets.push_back(std::move(rs));
917914
}
918915
}
@@ -1030,12 +1027,10 @@ Status OlapScanLocalState::prepare(RuntimeState* state) {
10301027
// For time-travel reads with a manifest, augment the version path with
10311028
// pre-compaction rowsets without modifying the shared tablet state.
10321029
if (!_tablets[i].tt_extra_rowsets.empty() || _tablets[i].tt_has_manifest) {
1033-
auto cloud_tablet =
1034-
std::dynamic_pointer_cast<CloudTablet>(_tablets[i].tablet);
1030+
auto cloud_tablet = std::dynamic_pointer_cast<CloudTablet>(_tablets[i].tablet);
10351031
RETURN_IF_ERROR(cloud_tablet->capture_rs_readers_with_tt_rowsets(
10361032
{0, _tablets[i].version}, _tablets[i].tt_extra_rowsets,
1037-
&_read_sources[i].rs_splits,
1038-
_state->skip_missing_version()));
1033+
&_read_sources[i].rs_splits, _state->skip_missing_version()));
10391034
} else {
10401035
_read_sources[i] = DORIS_TRY(_tablets[i].tablet->capture_read_source(
10411036
{0, _tablets[i].version},
@@ -1044,14 +1039,12 @@ Status OlapScanLocalState::prepare(RuntimeState* state) {
10441039
config::enable_fetch_rowsets_from_peer_replicas,
10451040
.capture_row_binlog = olap_scan_node().__isset.read_row_binlog &&
10461041
olap_scan_node().read_row_binlog,
1047-
.enable_prefer_cached_rowset =
1048-
config::is_cloud_mode()
1049-
? _state->enable_prefer_cached_rowset()
1050-
: false,
1042+
.enable_prefer_cached_rowset = config::is_cloud_mode()
1043+
? _state->enable_prefer_cached_rowset()
1044+
: false,
10511045
.query_freshness_tolerance_ms =
1052-
config::is_cloud_mode()
1053-
? _state->query_freshness_tolerance_ms()
1054-
: -1}));
1046+
config::is_cloud_mode() ? _state->query_freshness_tolerance_ms()
1047+
: -1}));
10551048
}
10561049
if (!PipelineXLocalState<>::_state->skip_delete_predicate()) {
10571050
_read_sources[i].fill_delete_predicates();

cloud/src/meta-service/meta_service.cpp

Lines changed: 21 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,7 @@ static int64_t resolve_version_at_time(Transaction* txn, const std::string& inst
405405
FullRangeGetOptions options;
406406
options.reverse = true;
407407
options.begin_key_selector = RangeKeySelector::FIRST_GREATER_OR_EQUAL;
408-
options.end_key_selector = RangeKeySelector::FIRST_GREATER_THAN;
408+
options.end_key_selector = RangeKeySelector::FIRST_GREATER_THAN;
409409

410410
std::unique_ptr<FullRangeGetIterator> iter =
411411
txn->full_range_get(begin_key, end_key, std::move(options));
@@ -414,7 +414,7 @@ static int64_t resolve_version_at_time(Transaction* txn, const std::string& inst
414414
auto item = iter->next();
415415
if (!item.has_value()) break;
416416

417-
std::string_view raw_key = item->first;
417+
std::string_view raw_key = item->first;
418418
std::string_view raw_value = item->second;
419419

420420
// Strip the trailing versionstamp suffix before deserialising the value.
@@ -464,7 +464,7 @@ void MetaServiceImpl::get_version_at_time(::google::protobuf::RpcController* con
464464
return;
465465
}
466466

467-
int64_t timestamp_ms = request->timestamp_ms();
467+
int64_t timestamp_ms = request->timestamp_ms();
468468
int32_t retention_days = request->has_retention_days() ? request->retention_days() : 90;
469469

470470
// Validate timestamp window — same logic for both single and batch.
@@ -482,8 +482,8 @@ void MetaServiceImpl::get_version_at_time(::google::protobuf::RpcController* con
482482
// Allow configurable clock skew to prevent spurious future-timestamp rejections.
483483
if (timestamp_ms > now_ms + config::time_travel_clock_skew_tolerance_ms) {
484484
code = MetaServiceCode::INVALID_ARGUMENT;
485-
msg = fmt::format("Requested timestamp {} ms is in the future (now={} ms).",
486-
timestamp_ms, now_ms);
485+
msg = fmt::format("Requested timestamp {} ms is in the future (now={} ms).", timestamp_ms,
486+
now_ms);
487487
return;
488488
}
489489

@@ -510,9 +510,9 @@ void MetaServiceImpl::get_version_at_time(::google::protobuf::RpcController* con
510510
std::vector<int64_t> resolved_times(n);
511511
for (int i = 0; i < n; ++i) {
512512
int64_t update_time_ms = -1;
513-
int64_t version = resolve_version_at_time(txn.get(), instance_id,
514-
request->partition_ids(i),
515-
timestamp_ms, &update_time_ms);
513+
int64_t version =
514+
resolve_version_at_time(txn.get(), instance_id, request->partition_ids(i),
515+
timestamp_ms, &update_time_ms);
516516
if (version == -2) {
517517
code = MetaServiceCode::KV_TXN_GET_ERR;
518518
msg = fmt::format("FDB scan error during time travel for partition {}",
@@ -543,30 +543,29 @@ void MetaServiceImpl::get_version_at_time(::google::protobuf::RpcController* con
543543
// Each encoded int64 in the key is 9 bytes (1 tag + 8 data).
544544
std::string ck_begin, ck_end;
545545
tt_compaction_key({instance_id, tablet_id, 0, 0}, &ck_begin);
546-
tt_compaction_key({instance_id, tablet_id,
547-
std::numeric_limits<int64_t>::max(),
548-
std::numeric_limits<int64_t>::max()}, &ck_end);
546+
tt_compaction_key({instance_id, tablet_id, std::numeric_limits<int64_t>::max(),
547+
std::numeric_limits<int64_t>::max()},
548+
&ck_end);
549549
ck_end.push_back('\xff');
550550

551551
std::unique_ptr<RangeGetIterator> ck_iter;
552552
bool found_checkpoint = false;
553-
if (txn->get(ck_begin, ck_end, &ck_iter) == TxnErrorCode::TXN_OK
554-
&& ck_iter) {
553+
if (txn->get(ck_begin, ck_end, &ck_iter) == TxnErrorCode::TXN_OK && ck_iter) {
555554
// Trailing key layout: start_v(9) + end_v(9) = 18 bytes.
556555
int64_t best_start = INT64_MAX;
557556
std::string best_val;
558557
while (ck_iter->has_next()) {
559558
auto [k, v] = ck_iter->next();
560559
if (k.size() < 18) continue;
561560
std::string_view sv = k.substr(k.size() - 18, 9);
562-
std::string_view ev = k.substr(k.size() - 9, 9);
561+
std::string_view ev = k.substr(k.size() - 9, 9);
563562
int64_t start_v = 0, end_v = 0;
564563
if (decode_int64(&sv, &start_v) != 0) continue;
565-
if (decode_int64(&ev, &end_v) != 0) continue;
564+
if (decode_int64(&ev, &end_v) != 0) continue;
566565
if (start_v <= version && version <= end_v) {
567566
if (start_v < best_start) {
568567
best_start = start_v;
569-
best_val = std::string(v);
568+
best_val = std::string(v);
570569
}
571570
found_checkpoint = true;
572571
}
@@ -605,8 +604,7 @@ void MetaServiceImpl::get_version_at_time(::google::protobuf::RpcController* con
605604
}
606605

607606
int64_t update_time_ms = -1;
608-
int64_t version = resolve_version_at_time(txn.get(), instance_id,
609-
request->partition_id(),
607+
int64_t version = resolve_version_at_time(txn.get(), instance_id, request->partition_id(),
610608
timestamp_ms, &update_time_ms);
611609
if (version == -2) {
612610
code = MetaServiceCode::KV_TXN_GET_ERR;
@@ -626,10 +624,10 @@ void MetaServiceImpl::get_version_at_time(::google::protobuf::RpcController* con
626624
response->set_version_update_time_ms(update_time_ms);
627625
}
628626

629-
void MetaServiceImpl::disable_time_travel_table(
630-
::google::protobuf::RpcController* controller,
631-
const DisableTimeTravelTableRequest* request, DisableTimeTravelTableResponse* response,
632-
::google::protobuf::Closure* done) {
627+
void MetaServiceImpl::disable_time_travel_table(::google::protobuf::RpcController* controller,
628+
const DisableTimeTravelTableRequest* request,
629+
DisableTimeTravelTableResponse* response,
630+
::google::protobuf::Closure* done) {
633631
RPC_PREPROCESS(disable_time_travel_table, get, del);
634632

635633
if (!request->has_table_id() || request->table_id() <= 0) {
@@ -656,8 +654,7 @@ void MetaServiceImpl::disable_time_travel_table(
656654
err = txn->commit();
657655
if (err != TxnErrorCode::TXN_OK) {
658656
code = cast_as<ErrCategory::COMMIT>(err);
659-
msg = fmt::format("failed to remove time travel marker, table_id={} err={}", table_id,
660-
err);
657+
msg = fmt::format("failed to remove time travel marker, table_id={} err={}", table_id, err);
661658
return;
662659
}
663660

0 commit comments

Comments
 (0)