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

gavinchou 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 9dbb0e93b27 [fix](cloud) Retain instance tombstones after recycle 
cleanup (#66870)
9dbb0e93b27 is described below

commit 9dbb0e93b27998088e6eb9afd0040758957ae0f2
Author: Yixuan Wang <[email protected]>
AuthorDate: Thu Aug 20 17:45:25 2026 +0800

    [fix](cloud) Retain instance tombstones after recycle cleanup (#66870)
    
    Related PR: https://github.com/apache/doris/pull/66519
    
    Problem Summary:
    The recycler previously removed the instance key after the recycle state
    reached INSTANCE_RECYCLE_STATE_CLEANUP_COMPLETED. This made the final
    recycle state unavailable and prevented successor-chain checks from
    distinguishing a completed successor from a missing instance. This
    change retains the deleted instance record as a tombstone, skips
    completed tombstones during scanning, excludes deleted instances from
    ResourceManager runtime indexes and caches, and allows predecessors to
    continue once their successor has completed cleanup.
---
 cloud/src/common/config.h                        |  1 +
 cloud/src/meta-service/meta_service.cpp          | 23 +++++---
 cloud/src/meta-service/meta_service_resource.cpp | 31 ++++++++---
 cloud/src/recycler/recycler.cpp                  | 28 ++++++++--
 cloud/src/resource-manager/resource_manager.cpp  | 33 +++++++++---
 cloud/src/resource-manager/resource_manager.h    |  2 +-
 cloud/test/meta_service_test.cpp                 | 55 +++++++++++++++++++
 cloud/test/recycle_versioned_keys_test.cpp       | 11 +++-
 cloud/test/recycler_operation_log_test.cpp       |  2 +-
 cloud/test/recycler_test.cpp                     | 68 +++++++++++++++++++++++-
 cloud/test/resource_test.cpp                     | 44 +++++++++++++++
 11 files changed, 269 insertions(+), 29 deletions(-)

diff --git a/cloud/src/common/config.h b/cloud/src/common/config.h
index 070cdd4e44e..c147f910d97 100644
--- a/cloud/src/common/config.h
+++ b/cloud/src/common/config.h
@@ -136,6 +136,7 @@ CONF_mInt32(scan_instances_interval_seconds, "60"); // 1min
 CONF_mInt32(check_object_interval_seconds, "43200"); // 12hours
 // enable recycler metrics statistics
 CONF_Bool(enable_recycler_stats_metrics, "false");
+CONF_mBool(retain_deleted_instance_tombstone, "true");
 
 CONF_mInt64(check_recycle_task_interval_seconds, "600"); // 10min
 CONF_mInt64(recycler_sleep_before_scheduling_seconds, "60");
diff --git a/cloud/src/meta-service/meta_service.cpp 
b/cloud/src/meta-service/meta_service.cpp
index 4eac97d5d57..c6aa71e38c8 100644
--- a/cloud/src/meta-service/meta_service.cpp
+++ b/cloud/src/meta-service/meta_service.cpp
@@ -120,12 +120,23 @@ std::string get_instance_id(const 
std::shared_ptr<ResourceManager>& rc_mgr,
             return "";
         }
 
-        // check instance_id valid by get fdb
-        if (config::enable_check_instance_id && 
!rc_mgr->is_instance_id_registered(id)) {
-            LOG(WARNING) << "use degraded format cloud_unique_id, but check 
instance failed, "
-                            "cloud_unique_id="
-                         << cloud_unique_id;
-            return "";
+        if (config::enable_check_instance_id) {
+            InstanceInfoPB instance;
+            auto [code, msg] = rc_mgr->get_instance(nullptr, id, &instance);
+            { TEST_SYNC_POINT_CALLBACK("is_instance_id_registered", &code); }
+            if (code == TxnErrorCode::TXN_KEY_NOT_FOUND) {
+                LOG(WARNING)
+                        << "use degraded format cloud_unique_id, but instance 
is not registered, "
+                           "cloud_unique_id="
+                        << cloud_unique_id;
+                return "";
+            }
+            if (instance.has_status() && instance.status() == 
InstanceInfoPB::DELETED) {
+                msg = "instance status has been set delete, plz check it, 
recycle_state=" +
+                      InstanceRecycleState_Name(instance.recycle_state());
+                LOG(WARNING) << msg << ", cloud_unique_id=" << cloud_unique_id;
+                return "";
+            }
         }
         return id;
     }
diff --git a/cloud/src/meta-service/meta_service_resource.cpp 
b/cloud/src/meta-service/meta_service_resource.cpp
index ecdb13a63c1..0284d4246ef 100644
--- a/cloud/src/meta-service/meta_service_resource.cpp
+++ b/cloud/src/meta-service/meta_service_resource.cpp
@@ -3554,13 +3554,30 @@ void 
MetaServiceImpl::alter_cluster(google::protobuf::RpcController* controller,
     if (!cloud_unique_id.empty() && instance_id.empty()) {
         auto [is_degraded_format, id] =
                 
ResourceManager::get_instance_id_by_cloud_unique_id(cloud_unique_id);
-        if (config::enable_check_instance_id && is_degraded_format &&
-            !resource_mgr_->is_instance_id_registered(id)) {
-            msg = "use degrade cloud_unique_id, but instance_id invalid, 
cloud_unique_id=" +
-                  cloud_unique_id;
-            LOG(WARNING) << msg;
-            code = MetaServiceCode::INVALID_ARGUMENT;
-            return;
+        if (config::enable_check_instance_id && is_degraded_format) {
+            InstanceInfoPB instance;
+            auto [code1, get_msg] = resource_mgr_->get_instance(nullptr, id, 
&instance);
+            { TEST_SYNC_POINT_CALLBACK("is_instance_id_registered", &code1); }
+            if (code1 == TxnErrorCode::TXN_KEY_NOT_FOUND) {
+                msg = "use degrade cloud_unique_id, but instance_id is not 
registered, "
+                      "cloud_unique_id=" +
+                      cloud_unique_id;
+                LOG(WARNING) << msg;
+                code = MetaServiceCode::INVALID_ARGUMENT;
+                return;
+            }
+            if (instance.has_status() && instance.status() == 
InstanceInfoPB::DELETED) {
+                msg = "instance status has been set delete, plz check it, 
recycle_state=" +
+                      InstanceRecycleState_Name(instance.recycle_state());
+                LOG(WARNING) << "use degraded format cloud_unique_id, but 
check instance failed, "
+                                "cloud_unique_id="
+                             << cloud_unique_id << " code=" << code1 << " 
info=" << get_msg
+                             << " status=" << instance.status() << " 
recycle_state="
+                             << 
InstanceRecycleState_Name(instance.recycle_state());
+                LOG(WARNING) << msg;
+                code = MetaServiceCode::INVALID_ARGUMENT;
+                return;
+            }
         }
         instance_id = get_instance_id(resource_mgr_, cloud_unique_id);
         if (instance_id.empty()) {
diff --git a/cloud/src/recycler/recycler.cpp b/cloud/src/recycler/recycler.cpp
index eac24365ebc..4efbf55004e 100644
--- a/cloud/src/recycler/recycler.cpp
+++ b/cloud/src/recycler/recycler.cpp
@@ -849,7 +849,7 @@ int InstanceRecycler::do_recycle() {
 /**
 * 1. delete all remote data
 * 2. delete all kv
-* 3. remove instance kv
+* 3. remove instance kv depend on config::retain_deleted_instance_tombstone
 */
 int InstanceRecycler::recycle_deleted_instance() {
     LOG_WARNING("begin to recycle deleted instance").tag("instance_id", 
instance_id_);
@@ -858,6 +858,11 @@ int InstanceRecycler::recycle_deleted_instance() {
     auto start_time = steady_clock::now();
     const auto recycle_state = instance_info_.recycle_state();
 
+    if (config::retain_deleted_instance_tombstone &&
+        recycle_state == 
InstanceRecycleState::INSTANCE_RECYCLE_STATE_CLEANUP_COMPLETED) {
+        return 0;
+    }
+
     DORIS_CLOUD_DEFER {
         auto cost = duration<float>(steady_clock::now() - start_time).count();
         if (ret != 0) {
@@ -998,13 +1003,26 @@ int 
InstanceRecycler::recycle_deleted_instance_metadata() {
             return -1;
         }
 
+        InstanceInfoPB successor_instance;
         std::string value;
         err = txn->get(key, &value);
         if (err == TxnErrorCode::TXN_OK) {
-            LOG(INFO) << "instance successor instance is still exist, skip 
deleting kv,"
-                      << " instance_id=" << instance_id_
-                      << " successor_instance_id=" << 
instance_info_.successor_instance_id();
-            return 0;
+            InstanceInfoPB successor_instance;
+            if (!successor_instance.ParseFromString(value)) {
+                LOG(WARNING) << "failed to parse successor instance, 
instance_id=" << instance_id_
+                             << " successor_instance_id=" << 
instance_info_.successor_instance_id();
+                return -1;
+            }
+            if (!successor_instance.has_recycle_state() ||
+                successor_instance.recycle_state() !=
+                        
InstanceRecycleState::INSTANCE_RECYCLE_STATE_CLEANUP_COMPLETED) {
+                LOG(INFO) << "instance successor has not completed recycling, 
skip deleting kv,"
+                          << " instance_id=" << instance_id_
+                          << " successor_instance_id=" << 
instance_info_.successor_instance_id()
+                          << " successor_status=" << 
successor_instance.status()
+                          << " successor_recycled_state=" << 
successor_instance.recycle_state();
+                return 0;
+            }
         } else if (err != TxnErrorCode::TXN_KEY_NOT_FOUND) {
             LOG(WARNING) << "failed to get successor instance, instance_id=" 
<< instance_id_
                          << " successor_instance_id=" << 
instance_info_.successor_instance_id()
diff --git a/cloud/src/resource-manager/resource_manager.cpp 
b/cloud/src/resource-manager/resource_manager.cpp
index fd634fdcff2..3c5f52a7bad 100644
--- a/cloud/src/resource-manager/resource_manager.cpp
+++ b/cloud/src/resource-manager/resource_manager.cpp
@@ -109,6 +109,9 @@ int ResourceManager::init() {
 
     std::unique_lock l(mtx_);
     for (auto& [inst_id, inst] : instances) {
+        if (inst.status() == InstanceInfoPB::DELETED) {
+            continue;
+        }
         for (auto& c : inst.clusters()) {
             add_cluster_to_index_no_lock(inst_id, c);
         }
@@ -193,12 +196,22 @@ bool ResourceManager::validate_nodes(const ClusterPB& 
cluster, std::string* err,
         // check here cloud_unique_id
         std::string cloud_unique_id = n.cloud_unique_id();
         auto [is_degrade_format, instance_id] = 
get_instance_id_by_cloud_unique_id(cloud_unique_id);
-        if (config::enable_check_instance_id && is_degrade_format &&
-            !is_instance_id_registered(instance_id)) {
-            ss << "node=" << n.DebugString()
-               << " cloud_unique_id use degrade format, but check instance 
failed";
-            *err = ss.str();
-            return false;
+        if (config::enable_check_instance_id && is_degrade_format) {
+            InstanceInfoPB instance;
+            auto [code, msg] = get_instance(nullptr, instance_id, &instance);
+            { TEST_SYNC_POINT_CALLBACK("is_instance_id_registered", &code); }
+            if (code == TxnErrorCode::TXN_KEY_NOT_FOUND) {
+                *err = "node=" + n.DebugString() +
+                       " cloud_unique_id use degrade format, but instance is 
not registered";
+                LOG(WARNING) << *err << ", cloud_unique_id=" << 
cloud_unique_id;
+                return false;
+            }
+            if (instance.has_status() && instance.status() == 
InstanceInfoPB::DELETED) {
+                *err = "instance status has been set delete, plz check it, 
recycle_state=" +
+                       InstanceRecycleState_Name(instance.recycle_state());
+                LOG(WARNING) << *err << ", cloud_unique_id=" << 
cloud_unique_id;
+                return false;
+            }
         }
         if (ClusterPB::SQL == cluster.type() && n.has_edit_log_port() && 
n.edit_log_port() &&
             n.has_node_type() &&
@@ -371,7 +384,7 @@ bool ResourceManager::is_instance_id_registered(const 
std::string& instance_id)
         LOG(WARNING) << "failed to check instance instance_id=" << instance_id
                      << ", code=" << format_as(c0) << ", info=" + m0;
     }
-    return c0 == TxnErrorCode::TXN_OK;
+    return c0 != TxnErrorCode::TXN_KEY_NOT_FOUND;
 }
 
 /**
@@ -1429,6 +1442,12 @@ void ResourceManager::refresh_instance(const 
std::string& instance_id,
         }
     }
 
+    if (instance.status() == InstanceInfoPB::DELETED) {
+        instance_multi_version_status_.erase(instance_id);
+        instance_source_snapshot_info_.erase(instance_id);
+        return;
+    }
+
     // If successor_instance_id is set, it means this instance has a successor 
instance,
     // so we do not need to add its clusters to the index again.
     if (!instance.has_successor_instance_id()) {
diff --git a/cloud/src/resource-manager/resource_manager.h 
b/cloud/src/resource-manager/resource_manager.h
index aaf27b8a88d..ca33a3f0983 100644
--- a/cloud/src/resource-manager/resource_manager.h
+++ b/cloud/src/resource-manager/resource_manager.h
@@ -191,7 +191,7 @@ public:
      *
      * @param instance_id
      *
-     * @return true, instance_id in fdb kv
+     * @return true if the instance kv exists.
      */
     bool is_instance_id_registered(const std::string& instance_id);
 
diff --git a/cloud/test/meta_service_test.cpp b/cloud/test/meta_service_test.cpp
index 22aa756309a..0e5ad0f910b 100644
--- a/cloud/test/meta_service_test.cpp
+++ b/cloud/test/meta_service_test.cpp
@@ -459,6 +459,61 @@ TEST(MetaServiceTest, GetInstanceIdTest) {
     sp->disable_processing();
 }
 
+TEST(MetaServiceTest, CheckInstanceRecycleCompletedWithRetainedKey) {
+    auto txn_kv = 
std::dynamic_pointer_cast<TxnKv>(std::make_shared<MemTxnKv>());
+    ASSERT_NE(txn_kv, nullptr);
+    ASSERT_EQ(txn_kv->init(), 0);
+    auto resource_mgr = std::make_shared<ResourceManager>(txn_kv);
+    ASSERT_EQ(resource_mgr->init(), 0);
+    auto rate_limiter = std::make_shared<RateLimiter>();
+    auto snapshot_manager = std::make_shared<SnapshotManager>(txn_kv);
+    MetaServiceImpl meta_service(txn_kv, resource_mgr, rate_limiter, 
snapshot_manager);
+
+    const std::string instance_id = "retained_recycle_instance";
+    InstanceInfoPB instance;
+    instance.set_instance_id(instance_id);
+    instance.set_status(InstanceInfoPB::DELETED);
+    
instance.set_recycle_state(InstanceRecycleState::INSTANCE_RECYCLE_STATE_CLEANUP_COMPLETED);
+
+    std::unique_ptr<Transaction> txn;
+    ASSERT_EQ(txn_kv->create_txn(&txn), TxnErrorCode::TXN_OK);
+    txn->put(instance_key({instance_id}), instance.SerializeAsString());
+    ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
+
+    bool finished = false;
+    std::string reason;
+    auto [code, msg] = 
meta_service.check_instance_recycle_completed(instance_id, finished, reason);
+    ASSERT_EQ(code, MetaServiceCode::OK) << msg;
+    ASSERT_TRUE(finished);
+    ASSERT_TRUE(reason.empty());
+
+    instance.set_recycle_state(
+            
InstanceRecycleState::INSTANCE_RECYCLE_STATE_METADATA_CLEANUP_PENDING);
+    ASSERT_EQ(txn_kv->create_txn(&txn), TxnErrorCode::TXN_OK);
+    txn->put(instance_key({instance_id}), instance.SerializeAsString());
+    ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
+
+    finished = true;
+    reason.clear();
+    std::tie(code, msg) =
+            meta_service.check_instance_recycle_completed(instance_id, 
finished, reason);
+    ASSERT_EQ(code, MetaServiceCode::OK) << msg;
+    ASSERT_FALSE(finished);
+    ASSERT_FALSE(reason.empty());
+
+    ASSERT_EQ(txn_kv->create_txn(&txn), TxnErrorCode::TXN_OK);
+    txn->remove(instance_key({instance_id}));
+    ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
+
+    finished = false;
+    reason.clear();
+    std::tie(code, msg) =
+            meta_service.check_instance_recycle_completed(instance_id, 
finished, reason);
+    ASSERT_EQ(code, MetaServiceCode::OK) << msg;
+    ASSERT_TRUE(finished);
+    ASSERT_NE(reason.find("does not exist"), std::string::npos);
+}
+
 TEST(MetaServiceTest, CreateInstanceTest) {
     auto meta_service = get_meta_service();
 
diff --git a/cloud/test/recycle_versioned_keys_test.cpp 
b/cloud/test/recycle_versioned_keys_test.cpp
index 00b99573fad..1551f895c30 100644
--- a/cloud/test/recycle_versioned_keys_test.cpp
+++ b/cloud/test/recycle_versioned_keys_test.cpp
@@ -1894,8 +1894,17 @@ TEST(RecycleVersionedKeysTest, RecycleDeletedInstance) {
 
         std::string instance_key_st = instance_key(instance_id);
         std::string instance_key_ed = instance_key(instance_id + '\x00');
-        ASSERT_EQ(count_range(txn_kv.get(), instance_key_st, instance_key_ed), 
0)
+        ASSERT_EQ(count_range(txn_kv.get(), instance_key_st, instance_key_ed), 
1)
                 << dump_range(txn_kv.get());
+        std::unique_ptr<Transaction> instance_txn;
+        ASSERT_EQ(txn_kv->create_txn(&instance_txn), TxnErrorCode::TXN_OK);
+        std::string instance_value;
+        ASSERT_EQ(instance_txn->get(instance_key_st, &instance_value), 
TxnErrorCode::TXN_OK);
+        InstanceInfoPB retained_instance;
+        ASSERT_TRUE(retained_instance.ParseFromString(instance_value));
+        ASSERT_EQ(retained_instance.status(), InstanceInfoPB::DELETED);
+        ASSERT_EQ(retained_instance.recycle_state(),
+                  
InstanceRecycleState::INSTANCE_RECYCLE_STATE_CLEANUP_COMPLETED);
 
         for (int i = 1; i < rowsets.size(); ++i) {
             std::unique_ptr<ListIterator> list_iter;
diff --git a/cloud/test/recycler_operation_log_test.cpp 
b/cloud/test/recycler_operation_log_test.cpp
index becac15276d..a50ae4894ce 100644
--- a/cloud/test/recycler_operation_log_test.cpp
+++ b/cloud/test/recycler_operation_log_test.cpp
@@ -2582,7 +2582,7 @@ TEST(RecycleOperationLogTest, RecycleDeletedInstance) {
     ASSERT_EQ(recycler.recycle_deleted_instance(), 0);
 
     // Verify all data keys are deleted, keeping the instance status and 
instance_update keys.
-    ASSERT_EQ(count_range(txn_kv.get()), 1) << dump_range(txn_kv.get());
+    ASSERT_EQ(count_range(txn_kv.get()), 2) << dump_range(txn_kv.get());
 }
 
 // Test OperationLogRecycleChecker class
diff --git a/cloud/test/recycler_test.cpp b/cloud/test/recycler_test.cpp
index 65316ca06b1..c5a0a150b95 100644
--- a/cloud/test/recycler_test.cpp
+++ b/cloud/test/recycler_test.cpp
@@ -4980,7 +4980,12 @@ TEST(RecyclerTest, recycle_deleted_instance) {
         std::unique_ptr<Transaction> txn;
         ASSERT_EQ(TxnErrorCode::TXN_OK, txn_kv->create_txn(&txn));
         std::string value;
-        ASSERT_EQ(TxnErrorCode::TXN_KEY_NOT_FOUND, 
txn->get(instance_key({instance_id}), &value));
+        ASSERT_EQ(TxnErrorCode::TXN_OK, txn->get(instance_key({instance_id}), 
&value));
+        InstanceInfoPB retained_instance;
+        ASSERT_TRUE(retained_instance.ParseFromString(value));
+        ASSERT_EQ(retained_instance.status(), InstanceInfoPB::DELETED);
+        ASSERT_EQ(retained_instance.recycle_state(),
+                  
InstanceRecycleState::INSTANCE_RECYCLE_STATE_CLEANUP_COMPLETED);
     }
 
     // check if all the objects are deleted
@@ -5045,6 +5050,67 @@ TEST(RecyclerTest, recycle_deleted_instance) {
     }
 }
 
+TEST(RecyclerTest, recycle_deleted_instance_metadata_successor_state) {
+    auto run_case = [](const std::string& suffix, bool put_successor,
+                       InstanceRecycleState successor_state,
+                       InstanceRecycleState expected_predecessor_state,
+                       bool successor_has_recycle_state = true) {
+        auto txn_kv = std::make_shared<MemTxnKv>();
+        ASSERT_EQ(txn_kv->init(), 0);
+
+        const std::string predecessor_id = "predecessor_" + suffix;
+        const std::string successor_id = "successor_" + suffix;
+
+        InstanceInfoPB predecessor;
+        predecessor.set_instance_id(predecessor_id);
+        predecessor.set_status(InstanceInfoPB::DELETED);
+        predecessor.set_recycle_state(
+                
InstanceRecycleState::INSTANCE_RECYCLE_STATE_METADATA_CLEANUP_PENDING);
+        predecessor.set_successor_instance_id(successor_id);
+        put_instance_info(txn_kv.get(), predecessor);
+
+        if (put_successor) {
+            InstanceInfoPB successor;
+            successor.set_instance_id(successor_id);
+            successor.set_status(InstanceInfoPB::DELETED);
+            if (successor_has_recycle_state) {
+                successor.set_recycle_state(successor_state);
+            }
+            put_instance_info(txn_kv.get(), successor);
+        }
+
+        InstanceRecycler recycler(txn_kv, predecessor, thread_group,
+                                  std::make_shared<TxnLazyCommitter>(txn_kv));
+        ASSERT_EQ(recycler.recycle_deleted_instance_metadata(), 0);
+
+        std::unique_ptr<Transaction> txn;
+        ASSERT_EQ(txn_kv->create_txn(&txn), TxnErrorCode::TXN_OK);
+        std::string value;
+        ASSERT_EQ(txn->get(instance_key({predecessor_id}), &value), 
TxnErrorCode::TXN_OK);
+        ASSERT_TRUE(predecessor.ParseFromString(value));
+        ASSERT_EQ(predecessor.recycle_state(), expected_predecessor_state);
+        if (put_successor) {
+            ASSERT_EQ(txn->get(instance_key({successor_id}), &value), 
TxnErrorCode::TXN_OK);
+            InstanceInfoPB successor;
+            ASSERT_TRUE(successor.ParseFromString(value));
+            ASSERT_EQ(successor.has_recycle_state(), 
successor_has_recycle_state);
+        }
+    };
+
+    run_case("successor_completed", true,
+             InstanceRecycleState::INSTANCE_RECYCLE_STATE_CLEANUP_COMPLETED,
+             InstanceRecycleState::INSTANCE_RECYCLE_STATE_CLEANUP_COMPLETED);
+    run_case("successor_pending", true,
+             
InstanceRecycleState::INSTANCE_RECYCLE_STATE_METADATA_CLEANUP_PENDING,
+             
InstanceRecycleState::INSTANCE_RECYCLE_STATE_METADATA_CLEANUP_PENDING);
+    run_case("successor_missing", false,
+             
InstanceRecycleState::INSTANCE_RECYCLE_STATE_METADATA_CLEANUP_PENDING,
+             InstanceRecycleState::INSTANCE_RECYCLE_STATE_CLEANUP_COMPLETED);
+    run_case("successor_legacy_without_state", true,
+             InstanceRecycleState::INSTANCE_RECYCLE_STATE_DATA_CLEANUP_PENDING,
+             
InstanceRecycleState::INSTANCE_RECYCLE_STATE_METADATA_CLEANUP_PENDING, false);
+}
+
 TEST(RecyclerTest, init_deleted_instance_with_terminal_recycle_state) {
     auto txn_kv = 
std::dynamic_pointer_cast<TxnKv>(std::make_shared<MemTxnKv>());
     ASSERT_NE(txn_kv.get(), nullptr);
diff --git a/cloud/test/resource_test.cpp b/cloud/test/resource_test.cpp
index 2c02d9bfccf..a1208dbfbac 100644
--- a/cloud/test/resource_test.cpp
+++ b/cloud/test/resource_test.cpp
@@ -409,6 +409,50 @@ TEST(ResourceTest, RestartResourceManager) {
     sp->clear_all_call_backs();
 }
 
+TEST(ResourceTest, RefreshDeletedInstanceClearsRuntimeCaches) {
+    auto txn_kv = create_txn_kv();
+    ResourceManager resource_mgr(txn_kv);
+    ASSERT_EQ(resource_mgr.init(), 0);
+
+    const std::string instance_id = "deleted_cache_instance";
+    const std::string cloud_unique_id = "deleted_cache_cloud_unique_id";
+    InstanceInfoPB instance;
+    instance.set_instance_id(instance_id);
+    instance.set_status(InstanceInfoPB::NORMAL);
+    
instance.set_multi_version_status(MultiVersionStatus::MULTI_VERSION_ENABLED);
+    instance.set_source_instance_id("source_instance");
+    instance.set_source_snapshot_id("00000000000000000000");
+    auto* cluster = instance.add_clusters();
+    cluster->set_type(ClusterPB::COMPUTE);
+    cluster->set_cluster_id("cluster_id");
+    cluster->set_cluster_name("cluster_name");
+    auto* node = cluster->add_nodes();
+    node->set_cloud_unique_id(cloud_unique_id);
+    node->set_ip("127.0.0.1");
+    node->set_heartbeat_port(10000);
+
+    resource_mgr.refresh_instance(instance_id, instance);
+
+    std::vector<NodeInfo> nodes;
+    ASSERT_EQ(resource_mgr.get_node(cloud_unique_id, &nodes), "");
+    ASSERT_EQ(nodes.size(), 1);
+    ASSERT_TRUE(resource_mgr.is_version_read_enabled(instance_id));
+    std::string source_instance_id;
+    Versionstamp source_snapshot_version;
+    ASSERT_TRUE(resource_mgr.get_source_snapshot_info(instance_id, 
&source_instance_id,
+                                                      
&source_snapshot_version));
+
+    instance.set_status(InstanceInfoPB::DELETED);
+    resource_mgr.refresh_instance(instance_id, instance);
+
+    nodes.clear();
+    EXPECT_EQ(resource_mgr.get_node(cloud_unique_id, &nodes), "cloud_unique_id 
not found");
+    EXPECT_TRUE(nodes.empty());
+    EXPECT_FALSE(resource_mgr.is_version_read_enabled(instance_id));
+    EXPECT_FALSE(resource_mgr.get_source_snapshot_info(instance_id, 
&source_instance_id,
+                                                       
&source_snapshot_version));
+}
+
 // test add/drop cluster
 TEST(ResourceTest, AddDropCluster) {
     auto sp = SyncPoint::get_instance();


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

Reply via email to