|
22 | 22 | #include <gtest/gtest.h> |
23 | 23 |
|
24 | 24 | #include <memory> |
| 25 | +#include <vector> |
25 | 26 |
|
26 | 27 | #include "cloud/cloud_cluster_info.h" |
27 | 28 | #include "cloud/cloud_storage_engine.h" |
@@ -96,6 +97,114 @@ class CloudSchemaChangeJobTest : public testing::Test { |
96 | 97 | std::shared_ptr<CloudClusterInfo> _cluster_info; |
97 | 98 | }; |
98 | 99 |
|
| 100 | +TEST_F(CloudSchemaChangeJobTest, FillVersionHolesBeforeNewTabletRunning) { |
| 101 | + int64_t base_tablet_id = 40001; |
| 102 | + int64_t new_tablet_id = 40002; |
| 103 | + |
| 104 | + TabletMetaSharedPtr base_meta(new TabletMeta( |
| 105 | + 1, 2, base_tablet_id, base_tablet_id + 100, 4, 5, TTabletSchema(), 6, {{7, 8}}, |
| 106 | + UniqueId(9, 10), TTabletType::TABLET_TYPE_DISK, TCompressionType::LZ4F)); |
| 107 | + TabletMetaSharedPtr new_meta(new TabletMeta( |
| 108 | + 1, 2, new_tablet_id, new_tablet_id + 100, 4, 5, TTabletSchema(), 6, {{7, 8}}, |
| 109 | + UniqueId(11, 12), TTabletType::TABLET_TYPE_DISK, TCompressionType::LZ4F)); |
| 110 | + |
| 111 | + auto base_tablet = std::make_shared<CloudTablet>(_engine, std::move(base_meta)); |
| 112 | + auto new_tablet = std::make_shared<CloudTablet>(_engine, std::move(new_meta)); |
| 113 | + static_cast<void>(new_tablet->set_tablet_state(TABLET_NOTREADY)); |
| 114 | + |
| 115 | + auto placeholder = create_rowset(new_tablet->tablet_schema(), new_tablet_id, 0, 1); |
| 116 | + auto rowset_after_hole = create_rowset(new_tablet->tablet_schema(), new_tablet_id, 4, 4); |
| 117 | + ASSERT_NE(placeholder, nullptr); |
| 118 | + ASSERT_NE(rowset_after_hole, nullptr); |
| 119 | + |
| 120 | + auto* sp = SyncPoint::get_instance(); |
| 121 | + sp->clear_all_call_backs(); |
| 122 | + sp->enable_processing(); |
| 123 | + |
| 124 | + sp->set_call_back("CloudMetaMgr::get_tablet_meta", [&](auto&& args) { |
| 125 | + auto tablet_id = try_any_cast<int64_t>(args[0]); |
| 126 | + auto* meta_ptr = try_any_cast<TabletMetaSharedPtr*>(args[1]); |
| 127 | + if (tablet_id == base_tablet_id) { |
| 128 | + *meta_ptr = base_tablet->tablet_meta(); |
| 129 | + } else if (tablet_id == new_tablet_id) { |
| 130 | + *meta_ptr = new_tablet->tablet_meta(); |
| 131 | + } |
| 132 | + try_any_cast_ret<Status>(args)->second = true; |
| 133 | + }); |
| 134 | + |
| 135 | + CloudTablet* loaded_new_tablet = nullptr; |
| 136 | + sp->set_call_back("CloudMetaMgr::sync_tablet_rowsets", [&](auto&& outcome) { |
| 137 | + auto* tablet = try_any_cast<CloudTablet*>(outcome[0]); |
| 138 | + if (tablet->tablet_id() == new_tablet_id) { |
| 139 | + loaded_new_tablet = tablet; |
| 140 | + std::unique_lock lock(tablet->get_header_lock()); |
| 141 | + std::vector<RowsetSharedPtr> rowsets; |
| 142 | + if (!tablet->rowset_map().count(Version(0, 1))) { |
| 143 | + rowsets.push_back(placeholder); |
| 144 | + } |
| 145 | + if (!tablet->rowset_map().count(Version(4, 4))) { |
| 146 | + rowsets.push_back(rowset_after_hole); |
| 147 | + } |
| 148 | + tablet->add_rowsets(std::move(rowsets), false, lock, false); |
| 149 | + } |
| 150 | + auto* pairs = try_any_cast_ret<Status>(outcome); |
| 151 | + pairs->second = true; |
| 152 | + pairs->first = Status::OK(); |
| 153 | + }); |
| 154 | + |
| 155 | + sp->set_call_back("CloudMetaMgr::prepare_tablet_job", [](auto&& outcome) { |
| 156 | + auto* pairs = try_any_cast_ret<Status>(outcome); |
| 157 | + pairs->second = true; |
| 158 | + pairs->first = Status::OK(); |
| 159 | + |
| 160 | + auto* resp = try_any_cast<cloud::StartTabletJobResponse*>(outcome[1]); |
| 161 | + resp->mutable_status()->set_code(cloud::MetaServiceCode::OK); |
| 162 | + resp->set_alter_version(2); |
| 163 | + }); |
| 164 | + |
| 165 | + bool commit_called = false; |
| 166 | + sp->set_call_back("CloudMetaMgr::commit_tablet_job", [&](auto&& outcome) { |
| 167 | + commit_called = true; |
| 168 | + auto* pairs = try_any_cast_ret<Status>(outcome); |
| 169 | + pairs->second = true; |
| 170 | + pairs->first = Status::OK(); |
| 171 | + |
| 172 | + auto* resp = try_any_cast<cloud::FinishTabletJobResponse*>(outcome[1]); |
| 173 | + resp->mutable_status()->set_code(cloud::MetaServiceCode::OK); |
| 174 | + auto* stats = resp->mutable_stats(); |
| 175 | + stats->set_num_rowsets(3); |
| 176 | + stats->set_num_segments(0); |
| 177 | + stats->set_num_rows(0); |
| 178 | + stats->set_data_size(0); |
| 179 | + }); |
| 180 | + |
| 181 | + TAlterTabletReqV2 request; |
| 182 | + request.base_tablet_id = base_tablet_id; |
| 183 | + request.new_tablet_id = new_tablet_id; |
| 184 | + request.alter_version = 1; |
| 185 | + request.__set_alter_tablet_type(TAlterTabletType::SCHEMA_CHANGE); |
| 186 | + |
| 187 | + CloudSchemaChangeJob sc_job(_engine, "test_fill_holes_before_running", 9999999999); |
| 188 | + auto status = sc_job.process_alter_tablet(request); |
| 189 | + |
| 190 | + ASSERT_TRUE(status.ok()) << status.to_string(); |
| 191 | + ASSERT_TRUE(commit_called); |
| 192 | + ASSERT_NE(loaded_new_tablet, nullptr); |
| 193 | + ASSERT_EQ(loaded_new_tablet->tablet_state(), TABLET_RUNNING); |
| 194 | + ASSERT_TRUE(loaded_new_tablet->rowset_map().count(Version(3, 3))); |
| 195 | + auto hole_rowset = loaded_new_tablet->rowset_map().at(Version(3, 3)); |
| 196 | + ASSERT_TRUE(hole_rowset->empty()); |
| 197 | + ASSERT_TRUE(hole_rowset->is_hole_rowset()); |
| 198 | + |
| 199 | + auto versions_result = |
| 200 | + loaded_new_tablet->capture_consistent_versions_unlocked(Version(3, 4), {}); |
| 201 | + ASSERT_TRUE(versions_result.has_value()) << versions_result.error(); |
| 202 | + const auto& versions = versions_result.value(); |
| 203 | + ASSERT_EQ(versions.size(), 2); |
| 204 | + ASSERT_EQ(versions[0], Version(3, 3)); |
| 205 | + ASSERT_EQ(versions[1], Version(4, 4)); |
| 206 | +} |
| 207 | + |
99 | 208 | // Test: cross-V1 compaction detected → abort SC job → return SC_COMPACTION_CONFLICT |
100 | 209 | TEST_F(CloudSchemaChangeJobTest, CrossV1CompactionDetected) { |
101 | 210 | int64_t base_tablet_id = 10001; |
|
0 commit comments