This is an automated email from the ASF dual-hosted git repository.

luwei16 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/master by this push:
     new 27e68bde457 [fix](cloud) Exclude stale rowsets from schema change 
temporary tablet (#67351)
27e68bde457 is described below

commit 27e68bde457e15590fb5dfa2df82d3d5ed8f9379
Author: Luwei <[email protected]>
AuthorDate: Tue Sep 1 17:56:59 2026 +0800

    [fix](cloud) Exclude stale rowsets from schema change temporary tablet 
(#67351)
    
    ### What problem does this PR solve?
    
    Issue Number: None
    
    Related PR: None
    
    Problem Summary: During merge-on-write cloud schema changes, the
    delete-bitmap temporary tablet copied stale rowset metadata from the
    real shadow tablet. Its version tracker could select a compacted stale
    edge that was absent from the active rowset map, causing schema change
    to fail with a rowset capture error under concurrent writes and
    compaction. Clear only the copied stale rowset metadata before
    constructing the temporary tablet so its tracker is built exclusively
    from active schema-change output and incremental rowsets, without
    altering the source tablet or shared delete-bitmap cache.
    
    ### Release note
    
    Fix cloud merge-on-write schema changes that could fail during
    concurrent writes and compaction.
    
    ### Check List (For Author)
    
    - Test: Unit Test
        - `./run-be-ut.sh --run --filter=CloudSchemaChangeJobTest.* -j 48`
        - `./build.sh --be -j 48`
    - Behavior changed: Yes. Cloud schema change temporary tablets no longer
    inherit stale rowset metadata.
    - Does this need documentation: No
---
 be/src/cloud/cloud_schema_change_job.cpp       |  3 ++
 be/src/storage/tablet/tablet_meta.cpp          |  6 ++-
 be/src/storage/tablet/tablet_meta.h            |  3 ++
 be/test/cloud/cloud_schema_change_job_test.cpp | 63 +++++++++++++++++++++++++-
 4 files changed, 73 insertions(+), 2 deletions(-)

diff --git a/be/src/cloud/cloud_schema_change_job.cpp 
b/be/src/cloud/cloud_schema_change_job.cpp
index 97f4e0ec538..427da01054d 100644
--- a/be/src/cloud/cloud_schema_change_job.cpp
+++ b/be/src/cloud/cloud_schema_change_job.cpp
@@ -585,6 +585,9 @@ Status CloudSchemaChangeJob::_process_delete_bitmap(int64_t 
alter_version,
             .tag("alter_version", alter_version);
     
RETURN_IF_ERROR(_cloud_storage_engine.register_compaction_stop_token(_new_tablet,
 initiator));
     TabletMetaSharedPtr tmp_meta = 
std::make_shared<TabletMeta>(*(_new_tablet->tablet_meta()));
+    // The temporary tablet must build its version graph only from active 
rowsets. Stale
+    // rowsets copied from the real tablet are not present in its active 
rowset map.
+    tmp_meta->clear_stale_rs_metas();
     tmp_meta->delete_bitmap().delete_bitmap.clear();
     // Keep only version [0-1] rowset, other rowsets will be added in 
_output_rowsets
     auto& rs_metas = tmp_meta->all_mutable_rs_metas();
diff --git a/be/src/storage/tablet/tablet_meta.cpp 
b/be/src/storage/tablet/tablet_meta.cpp
index 201e914463e..1f6ee1012da 100644
--- a/be/src/storage/tablet/tablet_meta.cpp
+++ b/be/src/storage/tablet/tablet_meta.cpp
@@ -1184,12 +1184,16 @@ Status TabletMeta::set_partition_id(int64_t 
partition_id) {
 }
 
 void TabletMeta::clear_stale_rowset() {
-    _stale_rs_metas.clear();
+    clear_stale_rs_metas();
     if (_enable_unique_key_merge_on_write) {
         _delete_bitmap->clear_rowset_cache_version();
     }
 }
 
+void TabletMeta::clear_stale_rs_metas() {
+    _stale_rs_metas.clear();
+}
+
 void TabletMeta::clear_rowsets() {
     _rs_metas.clear();
     if (_enable_unique_key_merge_on_write) {
diff --git a/be/src/storage/tablet/tablet_meta.h 
b/be/src/storage/tablet/tablet_meta.h
index 9b3f4764677..0efce3d3f2e 100644
--- a/be/src/storage/tablet/tablet_meta.h
+++ b/be/src/storage/tablet/tablet_meta.h
@@ -223,6 +223,9 @@ public:
     // used for after tablet cloned to clear stale rowset
     void clear_stale_rowset();
 
+    // Clear stale rowset metadata without changing the delete bitmap cache.
+    void clear_stale_rs_metas();
+
     void clear_rowsets();
 
     // MUST hold EXCLUSIVE `_meta_lock` in belonged Tablet
diff --git a/be/test/cloud/cloud_schema_change_job_test.cpp 
b/be/test/cloud/cloud_schema_change_job_test.cpp
index 9afce0bfb5c..2b7e7808367 100644
--- a/be/test/cloud/cloud_schema_change_job_test.cpp
+++ b/be/test/cloud/cloud_schema_change_job_test.cpp
@@ -153,9 +153,10 @@ public:
 
 protected:
     RowsetSharedPtr create_rowset(TabletSchemaSPtr schema, int64_t tablet_id, 
int64_t start,
-                                  int64_t end) {
+                                  int64_t end, int64_t rowset_id = 540081) 
const {
         RowsetMetaPB pb;
         json2pb::JsonToProtoMessage(_json_rowset_meta, &pb);
+        pb.set_rowset_id(rowset_id);
         pb.set_tablet_id(tablet_id);
         pb.set_start_version(start);
         pb.set_end_version(end);
@@ -173,6 +174,66 @@ protected:
     std::shared_ptr<CloudClusterInfo> _cluster_info;
 };
 
+// GTest assertion macros inflate cognitive complexity for this linear 
scenario.
+// NOLINTNEXTLINE(readability-function-cognitive-complexity)
+TEST_F(CloudSchemaChangeJobTest, 
DeleteBitmapTmpTabletDoesNotInheritStaleRowsets) {
+    constexpr int64_t new_tablet_id = 60002;
+
+    TabletMetaSharedPtr new_meta(new TabletMeta(
+            1, 2, new_tablet_id, new_tablet_id + 100, 4, 5, TTabletSchema(), 
6, {{7, 8}},
+            UniqueId(11, 12), TTabletType::TABLET_TYPE_DISK, 
TCompressionType::LZ4F, -1, true));
+    auto placeholder = create_rowset(new_meta->tablet_schema(), new_tablet_id, 
0, 1, 6000201);
+    auto compacted = create_rowset(new_meta->tablet_schema(), new_tablet_id, 
2, 3, 6000202);
+    auto sc_output_2 = create_rowset(new_meta->tablet_schema(), new_tablet_id, 
2, 2, 6000203);
+    auto sc_output_3 = create_rowset(new_meta->tablet_schema(), new_tablet_id, 
3, 3, 6000204);
+    auto sc_output_4 = create_rowset(new_meta->tablet_schema(), new_tablet_id, 
4, 4, 6000205);
+    ASSERT_NE(placeholder, nullptr);
+    ASSERT_NE(compacted, nullptr);
+    ASSERT_NE(sc_output_2, nullptr);
+    ASSERT_NE(sc_output_3, nullptr);
+    ASSERT_NE(sc_output_4, nullptr);
+
+    ASSERT_TRUE(new_meta->add_rs_meta(placeholder->rowset_meta()).ok());
+    ASSERT_TRUE(new_meta->add_rs_meta(compacted->rowset_meta()).ok());
+    new_meta->modify_rs_metas({}, {compacted->rowset_meta()});
+    ASSERT_EQ(new_meta->all_stale_rs_metas().size(), 1);
+
+    auto new_tablet = std::make_shared<CloudTablet>(_engine, new_meta);
+    auto* sp = SyncPoint::get_instance();
+    sp->clear_all_call_backs();
+    sp->enable_processing();
+    sp->set_call_back("CloudMetaMgr::prepare_tablet_job", [](auto&& outcome) {
+        auto* pairs = try_any_cast_ret<Status>(outcome);
+        pairs->second = true;
+        pairs->first = Status::OK();
+        auto* resp = try_any_cast<cloud::StartTabletJobResponse*>(outcome[1]);
+        resp->mutable_status()->set_code(cloud::MetaServiceCode::OK);
+    });
+
+    Status captured_status = Status::InternalError("temporary tablet was not 
inspected");
+    RowsetIdUnorderedSet captured_rowset_ids;
+    sp->set_call_back("CloudMetaMgr::sync_tablet_rowsets", [&](auto&& outcome) 
{
+        auto* tablet = try_any_cast<CloudTablet*>(outcome[0]);
+        std::shared_lock rlock(tablet->get_header_lock());
+        captured_status = tablet->get_all_rs_id_unlocked(4, 
&captured_rowset_ids);
+        auto* pairs = try_any_cast_ret<Status>(outcome);
+        pairs->second = true;
+        pairs->first = Status::InternalError("stop after inspecting temporary 
tablet");
+    });
+
+    CloudSchemaChangeJob sc_job(_engine, "test_tmp_tablet_stale_rowsets", 
9999999999);
+    sc_job._new_tablet = new_tablet;
+    sc_job._output_rowsets = {sc_output_2, sc_output_3, sc_output_4};
+    auto status = sc_job._process_delete_bitmap(4, 5, 12345, "");
+    ASSERT_TRUE(_engine.unregister_compaction_stop_token(new_tablet, 
false).ok());
+
+    ASSERT_FALSE(status.ok());
+    ASSERT_NE(status.to_string().find("stop after inspecting temporary 
tablet"), std::string::npos);
+    ASSERT_TRUE(captured_status.ok()) << captured_status.to_string();
+    ASSERT_EQ(captured_rowset_ids.size(), 3);
+    ASSERT_EQ(new_meta->all_stale_rs_metas().size(), 1);
+}
+
 TEST_F(CloudSchemaChangeJobTest, FillVersionHolesBeforeNewTabletRunning) {
     int64_t base_tablet_id = 40001;
     int64_t new_tablet_id = 40002;


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to