| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| |
| #include "cloud/cloud_internal_service.h" |
| |
| #include <chrono> |
| #include <condition_variable> |
| #include <functional> |
| #include <mutex> |
| #include <unordered_map> |
| |
| #include "cloud/cloud_storage_engine.h" |
| #include "cloud/cloud_tablet_mgr.h" |
| #include "cpp/sync_point.h" |
| #include "gen_cpp/Status_types.h" |
| #include "storage/tablet/tablet_meta.h" |
| #include "util/uid_util.h" |
| #include "util/work_thread_pool.hpp" |
| |
| namespace doris { |
| |
| namespace { |
| |
| class TestClosure final : public google::protobuf::Closure { |
| public: |
| explicit TestClosure(std::function<void()> callback) : _callback(std::move(callback)) {} |
| |
| void Run() override { |
| if (_callback) { |
| _callback(); |
| } |
| } |
| |
| private: |
| std::function<void()> _callback; |
| }; |
| |
| } // namespace |
| |
| class CloudInternalServiceTest : public testing::Test { |
| public: |
| CloudInternalServiceTest() : _engine(CloudStorageEngine(EngineOptions())) {} |
| |
| protected: |
| static constexpr int64_t kSyncedTabletId = 90001; |
| static constexpr int64_t kFailedTabletId = 90002; |
| static constexpr int64_t kUncachedTabletId = 90003; |
| |
| void SetUp() override { |
| auto sp = SyncPoint::get_instance(); |
| sp->clear_all_call_backs(); |
| sp->enable_processing(); |
| |
| sp->set_call_back("CloudMetaMgr::sync_tablet_rowsets", |
| [](auto&& args) { try_any_cast_ret<Status>(args)->second = true; }); |
| sp->set_call_back("CloudMetaMgr::get_tablet_meta", [this](auto&& args) { |
| const auto tablet_id = try_any_cast<int64_t>(args[0]); |
| auto* tablet_meta_ptr = try_any_cast<TabletMetaSharedPtr*>(args[1]); |
| auto* ret = try_any_cast_ret<Status>(args); |
| |
| auto& call_count = _tablet_meta_call_count[tablet_id]; |
| ++call_count; |
| if (tablet_id == kFailedTabletId && call_count >= 2) { |
| ret->first = Status::InternalError<false>("injected sync_meta failure"); |
| ret->second = true; |
| return; |
| } |
| |
| *tablet_meta_ptr = |
| create_tablet_meta(tablet_id, call_count >= 2 ? "time_series" : "size_based"); |
| ret->second = true; |
| }); |
| } |
| |
| void TearDown() override { |
| auto sp = SyncPoint::get_instance(); |
| sp->disable_processing(); |
| sp->clear_all_call_backs(); |
| } |
| |
| TabletMetaSharedPtr create_tablet_meta(int64_t tablet_id, std::string compaction_policy) { |
| TTabletSchema tablet_schema; |
| TColumn column; |
| column.__set_column_name("c1"); |
| column.__set_column_type(TColumnType()); |
| column.column_type.__set_type(TPrimitiveType::INT); |
| column.__set_is_key(true); |
| column.__set_aggregation_type(TAggregationType::NONE); |
| column.__set_col_unique_id(0); |
| tablet_schema.__set_columns({column}); |
| tablet_schema.__set_keys_type(TKeysType::DUP_KEYS); |
| |
| std::unordered_map<uint32_t, uint32_t> col_ordinal_to_unique_id = {{0, 0}}; |
| auto tablet_meta = std::make_shared<TabletMeta>( |
| 1, 2, tablet_id, 15674, 4, 5, tablet_schema, 1, col_ordinal_to_unique_id, |
| UniqueId(9, 10), TTabletType::TABLET_TYPE_DISK, TCompressionType::LZ4F); |
| tablet_meta->set_compaction_policy(std::move(compaction_policy)); |
| return tablet_meta; |
| } |
| |
| CloudStorageEngine _engine; |
| std::unordered_map<int64_t, int> _tablet_meta_call_count; |
| }; |
| |
| TEST_F(CloudInternalServiceTest, TestSyncTabletMetaCountsSyncedSkippedAndFailedTablets) { |
| auto synced_res = _engine.tablet_mgr().get_tablet(kSyncedTabletId); |
| ASSERT_TRUE(synced_res.has_value()) << synced_res.error(); |
| auto failed_res = _engine.tablet_mgr().get_tablet(kFailedTabletId); |
| ASSERT_TRUE(failed_res.has_value()) << failed_res.error(); |
| |
| auto cached_synced_tablet = synced_res.value(); |
| auto cached_failed_tablet = failed_res.value(); |
| ASSERT_EQ("size_based", cached_synced_tablet->tablet_meta()->compaction_policy()); |
| ASSERT_EQ("size_based", cached_failed_tablet->tablet_meta()->compaction_policy()); |
| |
| FifoThreadPool work_pool(1, 1, "sync_tablet_meta_test"); |
| PSyncTabletMetaRequest request; |
| request.add_tablet_ids(kSyncedTabletId); |
| request.add_tablet_ids(kUncachedTabletId); |
| request.add_tablet_ids(kFailedTabletId); |
| PSyncTabletMetaResponse response; |
| |
| std::mutex mutex; |
| std::condition_variable cv; |
| bool done = false; |
| TestClosure closure([&]() { |
| std::lock_guard lock(mutex); |
| done = true; |
| cv.notify_all(); |
| }); |
| |
| test_submit_sync_tablet_meta(_engine, work_pool, &request, &response, &closure); |
| |
| std::unique_lock lock(mutex); |
| ASSERT_TRUE(cv.wait_for(lock, std::chrono::seconds(5), [&] { return done; })); |
| |
| ASSERT_TRUE(response.has_status()); |
| EXPECT_EQ(TStatusCode::OK, response.status().status_code()) << response.status().DebugString(); |
| EXPECT_EQ(1, response.synced_tablets()); |
| EXPECT_EQ(1, response.skipped_tablets()); |
| EXPECT_EQ(1, response.failed_tablets()); |
| EXPECT_EQ("time_series", cached_synced_tablet->tablet_meta()->compaction_policy()); |
| EXPECT_EQ("size_based", cached_failed_tablet->tablet_meta()->compaction_policy()); |
| EXPECT_EQ(nullptr, _engine.tablet_mgr().get_tablet_if_cached(kUncachedTabletId)); |
| } |
| |
| TEST_F(CloudInternalServiceTest, TestSyncTabletMetaOfferFailure) { |
| FifoThreadPool work_pool(1, 0, "sync_tablet_meta_offer_failure_test"); |
| PSyncTabletMetaRequest request; |
| request.add_tablet_ids(kSyncedTabletId); |
| PSyncTabletMetaResponse response; |
| |
| bool done = false; |
| TestClosure closure([&]() { done = true; }); |
| |
| test_submit_sync_tablet_meta(_engine, work_pool, &request, &response, &closure); |
| |
| EXPECT_TRUE(done); |
| ASSERT_TRUE(response.has_status()); |
| EXPECT_EQ(TStatusCode::INTERNAL_ERROR, response.status().status_code()); |
| EXPECT_TRUE(response.status().error_msgs(0).find( |
| "failed to offer sync_tablet_meta request to work pool") != |
| std::string::npos); |
| } |
| |
| } // namespace doris |